Index: /trunk/include/VBox/HostServices/GuestControlSvc.h
===================================================================
--- /trunk/include/VBox/HostServices/GuestControlSvc.h	(revision 45414)
+++ /trunk/include/VBox/HostServices/GuestControlSvc.h	(revision 45415)
@@ -63,5 +63,5 @@
 /** Gets the session ID out of a context ID. */
 #define VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uContextID) \
-    ((uContextID) >> 27)
+    (((uContextID) >> 27) & 0x1f)
 /** Gets the process ID out of a context ID. */
 #define VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(uContextID) \
@@ -308,4 +308,10 @@
     GUEST_MSG_FILTER = 4,
     /**
+     * Skips the current assigned message returned by GUEST_MSG_WAIT.
+     * Needed for telling the host service to not keep stale
+     * host commands in the queue.
+     */
+    GUEST_MSG_SKIP = 5,
+    /**
      * Guest reports back a guest session status.
      */
@@ -414,6 +420,7 @@
 /**
  * Asks the guest control host service to set a command
- * filter for this client. The filter itself will affect
- * the context ID bound to a command.
+ * filter for this client. This filter will then only
+ * deliver messages to the client which match the
+ * wanted context ID (ranges).
  */
 typedef struct HGCMMsgCmdSetFilter
@@ -429,6 +436,17 @@
 
 /**
+ * Asks the guest control host service to skip the
+ * currently assigned host command returned by
+ * VbglR3GuestCtrlMsgWaitFor().
+ */
+typedef struct HGCMMsgCmdSkip
+{
+    VBoxGuestHGCMCallInfo hdr;
+
+} HGCMMsgCmdSkip;
+
+/**
  * Asks the guest control host service to cancel all pending (outstanding)
- * waits which were not processed yet.  This is handy for a graceful shutdown.
+ * waits which were not processed yet. This is handy for a graceful shutdown.
  */
 typedef struct HGCMMsgCancelPendingWaits
Index: /trunk/include/VBox/VBoxGuestLib.h
===================================================================
--- /trunk/include/VBox/VBoxGuestLib.h	(revision 45414)
+++ /trunk/include/VBox/VBoxGuestLib.h	(revision 45415)
@@ -603,4 +603,5 @@
 VBGLR3DECL(int) VbglR3GuestCtrlMsgWaitFor(uint32_t uClientId, uint32_t *puMsg, uint32_t *puNumParms);
 VBGLR3DECL(int) VbglR3GuestCtrlMsgSetFilter(uint32_t uClientId, uint32_t uFilterAdd, uint32_t uFilterRemove);
+VBGLR3DECL(int) VbglR3GuestCtrlMsgSkip(uint32_t uClientId);
 VBGLR3DECL(int) VbglR3GuestCtrlCancelPendingWaits(uint32_t u32ClientId);
 /* Guest session handling. */
Index: /trunk/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibGuestCtrl.cpp
===================================================================
--- /trunk/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibGuestCtrl.cpp	(revision 45414)
+++ /trunk/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibGuestCtrl.cpp	(revision 45415)
@@ -156,4 +156,29 @@
     if (RT_SUCCESS(rc))
         rc = Msg.hdr.result;
+    return rc;
+}
+
+
+/**
+ * Tells the host service to skip the current message returned by
+ * VbglR3GuestCtrlMsgWaitFor().
+ *
+ * @return  IPRT status code.
+ * @param   uClientId       The client id returned by VbglR3GuestCtrlConnect().
+ */
+VBGLR3DECL(int) VbglR3GuestCtrlMsgSkip(uint32_t uClientId)
+{
+    HGCMMsgCmdSkip Msg;
+
+    Msg.hdr.result      = VERR_WRONG_ORDER;
+    Msg.hdr.u32ClientID = uClientId;
+    Msg.hdr.u32Function = GUEST_MSG_SKIP; /* Tell the host we want to skip
+                                             the current assigned command. */
+    Msg.hdr.cParms      = 0;              /* No parameters needed. */
+
+    int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
+    if (RT_SUCCESS(rc))
+        rc = Msg.hdr.result;
+
     return rc;
 }
Index: /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp
===================================================================
--- /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp	(revision 45414)
+++ /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp	(revision 45415)
@@ -274,5 +274,6 @@
                          * skip all not wanted messages here.
                          */
-                        VBoxServiceVerbose(3, "Skipping msg=%RU32 ...\n", uMsg);
+                        rc = VbglR3GuestCtrlMsgSkip(g_uControlSvcClientID);
+                        VBoxServiceVerbose(3, "Skipping msg=%RU32, rc=%Rrc\n", uMsg, rc);
                     }
                     break;
@@ -323,6 +324,6 @@
                            pHostCtx->uClientID, pHostCtx->uProtocol);
 
-        rc = GstCntlSessionThreadOpen(&g_lstControlSessionThreads,
-                                      &ssInfo, NULL /* Session */);
+        rc = GstCntlSessionThreadCreate(&g_lstControlSessionThreads,
+                                        &ssInfo, NULL /* Session */);
     }
 
@@ -350,5 +351,4 @@
 
     uint32_t uSessionID, uFlags;
-
     int rc = VbglR3GuestCtrlSessionGetClose(pHostCtx, &uFlags, &uSessionID);
     if (RT_SUCCESS(rc))
@@ -361,9 +361,9 @@
             if (pThread->StartupInfo.uSessionID == uSessionID)
             {
-                rc = GstCntlSessionThreadClose(pThread, uFlags);
+                rc = GstCntlSessionThreadDestroy(pThread, uFlags);
                 break;
             }
         }
-
+#if 0
         if (RT_FAILURE(rc))
         {
@@ -379,8 +379,11 @@
             }
         }
-    }
-
-    VBoxServiceVerbose(2, "Closing guest session %RU32 returned rc=%Rrc\n",
-                       uSessionID, rc);
+#endif
+        VBoxServiceVerbose(2, "Closing guest session %RU32 returned rc=%Rrc\n",
+                           uSessionID, rc);
+    }
+    else
+        VBoxServiceError("Closing guest session failed with rc=%Rrc\n", rc);
+
     return rc;
 }
@@ -420,6 +423,6 @@
     VBoxServiceVerbose(2, "Shutting down ...\n");
 
-    int rc2 = GstCntlSessionThreadCloseAll(&g_lstControlSessionThreads,
-                                           0 /* Flags */);
+    int rc2 = GstCntlSessionThreadDestroyAll(&g_lstControlSessionThreads,
+                                             0 /* Flags */);
     if (RT_FAILURE(rc2))
         VBoxServiceError("Closing session threads failed with rc=%Rrc\n", rc2);
Index: /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControl.h
===================================================================
--- /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControl.h	(revision 45414)
+++ /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControl.h	(revision 45415)
@@ -169,5 +169,6 @@
 /**
  * Structure for a guest session thread to
- * observe the forked session instance.
+ * observe/control the forked session instance from
+ * the VBoxService main executable.
  */
 typedef struct VBOXSERVICECTRLSESSIONTHREAD
@@ -243,16 +244,16 @@
 typedef struct VBOXSERVICECTRLSESSION
 {
+    /* The session's startup information. */
     VBOXSERVICECTRLSESSIONSTARTUPINFO
                                     StartupInfo;
-    /** List of active guest control threads (VBOXSERVICECTRLTHREAD). */
-    RTLISTANCHOR                    lstControlThreadsActive;
-    /** List of inactive guest control threads (VBOXSERVICECTRLTHREAD). */
+    /** List of active guest process threads (VBOXSERVICECTRLPROCESS). */
+    RTLISTANCHOR                    lstProcessesActive;
+    /** List of inactive guest process threads (VBOXSERVICECTRLPROCESS). */
     /** @todo Still needed? */
-    RTLISTANCHOR                    lstControlThreadsInactive;
+    RTLISTANCHOR                    lstProcessesInactive;
     /** List of guest control files (VBOXSERVICECTRLFILE). */
     RTLISTANCHOR                    lstFiles;
-    /** Critical section for protecting the guest process
-     *  threading list. */
-    RTCRITSECT                      csControlThreads;
+    /** The session's critical section. */
+    RTCRITSECT                      CritSect;
     /** Session flags. */
     uint32_t                        uFlags;
@@ -330,22 +331,13 @@
     /** Critical section for thread-safe use. */
     RTCRITSECT                      CritSect;
-    /** @todo Document me! */
+    /** Process startup information. */
+    VBOXSERVICECTRLPROCSTARTUPINFO
+                                    StartupInfo;
+    /** The process' PID assigned by the guest OS. */
     uint32_t                        uPID;
-    char                           *pszCmd;
-    uint32_t                        uFlags;
-    char                          **papszArgs;
-    uint32_t                        uNumArgs;
-    char                          **papszEnv;
-    uint32_t                        uNumEnvVars;
-    /** Name of specified user account to run the
-     *  guest process under. */
-    char                           *pszUser;
-    /** Password of specified user account. */
-    char                           *pszPassword;
-    /** Overall time limit (in ms) that the guest process
-     *  is allowed to run. 0 for indefinite time. */
-    uint32_t                        uTimeLimitMS;
     /** Pointer to the current IPC request being
-     *  processed. */
+     *  processed. We only support one request at a
+     *  time at the moment.
+     ** @todo Implemenet a request queue. */
     PVBOXSERVICECTRLREQUEST         pRequest;
     /** StdIn pipe for addressing writes to the
@@ -374,7 +366,7 @@
 
 /* Guest session thread handling. */
-extern int                      GstCntlSessionThreadOpen(PRTLISTANCHOR pList, const PVBOXSERVICECTRLSESSIONSTARTUPINFO pSessionStartupInfo, PVBOXSERVICECTRLSESSIONTHREAD *ppSessionThread);
-extern int                      GstCntlSessionThreadClose(PVBOXSERVICECTRLSESSIONTHREAD pSession, uint32_t uFlags);
-extern int                      GstCntlSessionThreadCloseAll(PRTLISTANCHOR pList, uint32_t uFlags);
+extern int                      GstCntlSessionThreadCreate(PRTLISTANCHOR pList, const PVBOXSERVICECTRLSESSIONSTARTUPINFO pSessionStartupInfo, PVBOXSERVICECTRLSESSIONTHREAD *ppSessionThread);
+extern int                      GstCntlSessionThreadDestroy(PVBOXSERVICECTRLSESSIONTHREAD pSession, uint32_t uFlags);
+extern int                      GstCntlSessionThreadDestroyAll(PRTLISTANCHOR pList, uint32_t uFlags);
 extern int                      GstCntlSessionThreadTerminate(PVBOXSERVICECTRLSESSIONTHREAD pSession);
 extern RTEXITCODE               VBoxServiceControlSessionForkInit(int argc, char **argv);
@@ -392,8 +384,8 @@
 extern int                      GstCntlProcessPerform(PVBOXSERVICECTRLPROCESS pProcess, PVBOXSERVICECTRLREQUEST pRequest);
 extern int                      GstCntlProcessStart(const PVBOXSERVICECTRLSESSION pSession, const PVBOXSERVICECTRLPROCSTARTUPINFO pStartupInfo, uint32_t uContext);
-extern int                      GstCntlProcessStop(const PVBOXSERVICECTRLPROCESS pThread);
-extern void                     GstCntlProcessRelease(const PVBOXSERVICECTRLPROCESS pThread);
-extern int                      GstCntlProcessWait(const PVBOXSERVICECTRLPROCESS pThread, RTMSINTERVAL msTimeout, int *prc);
-extern int                      GstCntlProcessFree(PVBOXSERVICECTRLPROCESS pThread);
+extern int                      GstCntlProcessStop(PVBOXSERVICECTRLPROCESS pProcess);
+extern void                     GstCntlProcessRelease(const PVBOXSERVICECTRLPROCESS pProcess);
+extern int                      GstCntlProcessWait(const PVBOXSERVICECTRLPROCESS pProcess, RTMSINTERVAL msTimeout, int *pRc);
+extern int                      GstCntlProcessFree(PVBOXSERVICECTRLPROCESS pProcess);
 /* Process request handling. */
 extern int                      GstCntlProcessRequestAlloc(PVBOXSERVICECTRLREQUEST *ppReq, VBOXSERVICECTRLREQUESTTYPE enmType);
Index: /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlProcess.cpp
===================================================================
--- /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlProcess.cpp	(revision 45414)
+++ /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlProcess.cpp	(revision 45415)
@@ -87,61 +87,14 @@
         return rc;
 
-    pProcess->uPID         = 0;          /* Don't have a PID yet. */
-    pProcess->pRequest     = NULL;       /* No request assigned yet. */
-    pProcess->uFlags       = pProcess->uFlags;
-    pProcess->uTimeLimitMS = (   pProcess->uTimeLimitMS == UINT32_MAX
-                              || pProcess->uTimeLimitMS == 0)
-                           ? RT_INDEFINITE_WAIT : pProcess->uTimeLimitMS;
-
-    /* Prepare argument list. */
-    pProcess->uNumArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */
-    rc = RTGetOptArgvFromString(&pProcess->papszArgs, (int*)&pProcess->uNumArgs,
-                                (pStartupInfo->uNumArgs > 0) ? pStartupInfo->szArgs : "", NULL);
-    /* Did we get the same result? */
-    Assert(pStartupInfo->uNumArgs == pProcess->uNumArgs);
-
-    if (RT_SUCCESS(rc))
-    {
-        /* Prepare environment list. */
-        pProcess->uNumEnvVars = 0;
-        if (pProcess->uNumEnvVars)
-        {
-            pProcess->papszEnv = (char **)RTMemAlloc(pStartupInfo->uNumEnvVars * sizeof(char*));
-            AssertPtr(pProcess->papszEnv);
-            pProcess->uNumEnvVars = pProcess->uNumEnvVars;
-
-            const char *pszCur = pStartupInfo->szEnv;
-            uint32_t i = 0;
-            uint32_t cbLen = 0;
-            while (cbLen < pStartupInfo->cbEnv)
-            {
-                /* sanity check */
-                if (i >= pStartupInfo->uNumEnvVars)
-                {
-                    rc = VERR_INVALID_PARAMETER;
-                    break;
-                }
-                int cbStr = RTStrAPrintf(&pProcess->papszEnv[i++], "%s", pszCur);
-                if (cbStr < 0)
-                {
-                    rc = VERR_NO_STR_MEMORY;
-                    break;
-                }
-                pszCur += cbStr + 1; /* Skip terminating '\0' */
-                cbLen  += cbStr + 1; /* Skip terminating '\0' */
-            }
-            Assert(i == pProcess->uNumEnvVars);
-        }
-
-        /* The actual command to execute. */
-        pProcess->pszCmd      = RTStrDup(pStartupInfo->szCmd);
-        AssertPtr(pProcess->pszCmd);
-
-        /* User management. */
-        pProcess->pszUser     = RTStrDup(pStartupInfo->szUser);
-        AssertPtr(pProcess->pszUser);
-        pProcess->pszPassword = RTStrDup(pStartupInfo->szPassword);
-        AssertPtr(pProcess->pszPassword);
-    }
+    pProcess->uPID     = 0;          /* Don't have a PID yet. */
+    pProcess->pRequest = NULL;       /* No request assigned yet. */
+
+    /* Copy over startup info. */
+    memcpy(&pProcess->StartupInfo, pStartupInfo, sizeof(VBOXSERVICECTRLPROCSTARTUPINFO));
+
+    /* Adjust timeout value. */
+    if (   pProcess->StartupInfo.uTimeLimitMS == UINT32_MAX
+        || pProcess->StartupInfo.uTimeLimitMS == 0)
+        pProcess->StartupInfo.uTimeLimitMS = RT_INDEFINITE_WAIT;
 
     if (RT_FAILURE(rc)) /* Clean up on failure. */
@@ -161,28 +114,7 @@
     AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
 
-    VBoxServiceVerbose(3, "[PID %u]: Freeing ...\n",
+    VBoxServiceVerbose(3, "[PID %RU32]: Freeing ...\n",
                        pProcess->uPID);
 
-    int rc = RTCritSectEnter(&pProcess->CritSect);
-    if (RT_SUCCESS(rc))
-    {
-        if (pProcess->uNumEnvVars)
-        {
-            for (uint32_t i = 0; i < pProcess->uNumEnvVars; i++)
-                RTStrFree(pProcess->papszEnv[i]);
-            RTMemFree(pProcess->papszEnv);
-        }
-        RTGetOptArgvFree(pProcess->papszArgs);
-
-        RTStrFree(pProcess->pszCmd);
-        RTStrFree(pProcess->pszUser);
-        RTStrFree(pProcess->pszPassword);
-
-        VBoxServiceVerbose(3, "[PID %u]: Setting stopped state\n",
-                           pProcess->uPID);
-
-        rc = RTCritSectLeave(&pProcess->CritSect);
-        AssertRC(rc);
-    }
 
     /*
@@ -198,5 +130,5 @@
     pProcess = NULL;
 
-    return rc;
+    return VINF_SUCCESS;
 }
 
@@ -207,32 +139,43 @@
  *
  * @return  IPRT status code.
- * @param   pThread             Thread to shut down.
- */
-int GstCntlProcessStop(const PVBOXSERVICECTRLPROCESS pThread)
-{
-    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
-
-    VBoxServiceVerbose(3, "[PID %u]: Stopping ...\n",
-                       pThread->uPID);
-
-    int rc = gstcntlProcessRequestCancel(pThread->pRequest);
-    if (RT_FAILURE(rc))
-        VBoxServiceError("[PID %u]: Signalling request event failed, rc=%Rrc\n",
-                         pThread->uPID, rc);
-
-    /* Do *not* set pThread->fShutdown or other stuff here!
-     * The guest thread loop will do that as soon as it processes the quit message. */
-
-    PVBOXSERVICECTRLREQUEST pRequest;
-    rc = GstCntlProcessRequestAlloc(&pRequest, VBOXSERVICECTRLREQUEST_QUIT);
+ * @param   pProcess            Process to stop.
+ */
+int GstCntlProcessStop(PVBOXSERVICECTRLPROCESS pProcess)
+{
+    AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+
+    VBoxServiceVerbose(3, "[PID %RU32]: Stopping ...\n",
+                       pProcess->uPID);
+
+    int rc = RTCritSectEnter(&pProcess->CritSect);
     if (RT_SUCCESS(rc))
     {
-        rc = GstCntlProcessPerform(pThread, pRequest);
-        if (RT_FAILURE(rc))
-            VBoxServiceVerbose(3, "[PID %u]: Sending quit request failed with rc=%Rrc\n",
-                               pThread->uPID, rc);
-
-        GstCntlProcessRequestFree(pRequest);
-    }
+        if (pProcess->pRequest)
+        {
+            rc = gstcntlProcessRequestCancel(pProcess->pRequest);
+            if (RT_FAILURE(rc))
+                VBoxServiceError("[PID %RU32]: Signalling request event failed, rc=%Rrc\n",
+                                 pProcess->uPID, rc);
+        }
+
+        /* Do *not* set pThread->fShutdown or other stuff here!
+         * The guest thread loop will do that as soon as it processes the quit message. */
+
+        PVBOXSERVICECTRLREQUEST pRequest;
+        rc = GstCntlProcessRequestAlloc(&pRequest, VBOXSERVICECTRLREQUEST_QUIT);
+        if (RT_SUCCESS(rc))
+        {
+            rc = GstCntlProcessPerform(pProcess, pRequest);
+            if (RT_FAILURE(rc))
+                VBoxServiceVerbose(3, "[PID %RU32]: Sending quit request failed with rc=%Rrc\n",
+                                   pProcess->uPID, rc);
+
+            GstCntlProcessRequestFree(pRequest);
+        }
+
+        int rc2 = RTCritSectLeave(&pProcess->CritSect);
+        AssertRC(rc2);
+    }
+
     return rc;
 }
@@ -242,11 +185,11 @@
  * Releases (unlocks) a previously locked guest process.
  *
- * @param   pThread                 Thread to unlock.
- */
-void GstCntlProcessRelease(const PVBOXSERVICECTRLPROCESS pThread)
-{
-    AssertPtr(pThread);
-
-    int rc = RTCritSectLeave(&pThread->CritSect);
+ * @param   pProcess            Process to unlock.
+ */
+void GstCntlProcessRelease(const PVBOXSERVICECTRLPROCESS pProcess)
+{
+    AssertPtrReturnVoid(pProcess);
+
+    int rc = RTCritSectLeave(&pProcess->CritSect);
     AssertRC(rc);
 }
@@ -255,39 +198,41 @@
 /**
  * Wait for a guest process thread to shut down.
- *
- * @return  IPRT status code.
- * @param   pThread             Thread to wait shutting down for.
+ * Note: Caller is responsible for locking!
+ *
+ * @return  IPRT status code.
+ * @param   pProcess            Process to wait shutting down for.
  * @param   RTMSINTERVAL        Timeout in ms to wait for shutdown.
- * @param   prc                 Where to store the thread's return code. Optional.
- */
-int GstCntlProcessWait(const PVBOXSERVICECTRLPROCESS pThread,
-                       RTMSINTERVAL msTimeout, int *prc)
-{
-    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
-    /* prc is optional. */
+ * @param   pRc                 Where to store the thread's return code. Optional.
+ */
+int GstCntlProcessWait(const PVBOXSERVICECTRLPROCESS pProcess,
+                       RTMSINTERVAL msTimeout, int *pRc)
+{
+    AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+    /* pRc is optional. */
 
     int rc = VINF_SUCCESS;
-    if (   pThread->Thread != NIL_RTTHREAD
-        && ASMAtomicReadBool(&pThread->fStarted))
-    {
-        VBoxServiceVerbose(2, "[PID %u]: Waiting for shutdown of pThread=0x%p = \"%s\"...\n",
-                           pThread->uPID, pThread, pThread->pszCmd);
+    if (   pProcess->Thread != NIL_RTTHREAD
+        && ASMAtomicReadBool(&pProcess->fStarted))
+    {
+        VBoxServiceVerbose(2, "[PID %RU32]: Waiting for shutdown (%RU32ms) ...\n",
+                           pProcess->uPID, msTimeout);
 
         /* Wait a bit ... */
         int rcThread;
-        rc = RTThreadWait(pThread->Thread, msTimeout, &rcThread);
+        rc = RTThreadWait(pProcess->Thread, msTimeout, &rcThread);
         if (RT_FAILURE(rc))
         {
-            VBoxServiceError("[PID %u]: Waiting for shutting down thread returned error rc=%Rrc\n",
-                             pThread->uPID, rc);
+            VBoxServiceError("[PID %RU32]: Waiting for shutting down thread returned error rc=%Rrc\n",
+                             pProcess->uPID, rc);
         }
         else
         {
-            VBoxServiceVerbose(3, "[PID %u]: Thread reported exit code=%Rrc\n",
-                               pThread->uPID, rcThread);
-            if (prc)
-                *prc = rcThread;
-        }
-    }
+            VBoxServiceVerbose(3, "[PID %RU32]: Thread reported exit code=%Rrc\n",
+                               pProcess->uPID, rcThread);
+            if (pRc)
+                *pRc = rcThread;
+        }
+    }
+
     return rc;
 }
@@ -368,5 +313,5 @@
         && cbReadable)
     {
-        VBoxServiceVerbose(3, "gstcntlProcessHandleOutputError: idPollHnd=%u has %ld bytes left, vetoing close\n",
+        VBoxServiceVerbose(3, "gstcntlProcessHandleOutputError: idPollHnd=%RU32 has %zu bytes left, vetoing close\n",
                            idPollHnd, cbReadable);
 
@@ -377,5 +322,5 @@
     }
     else
-        VBoxServiceVerbose(3, "gstcntlProcessHandleOutputError: idPollHnd=%u will be closed\n",
+        VBoxServiceVerbose(3, "gstcntlProcessHandleOutputError: idPollHnd=%RU32 will be closed\n",
                            idPollHnd);
 
@@ -460,6 +405,6 @@
     pRequest->rc = rc;
 
-#ifdef _DEBUG
-    VBoxServiceVerbose(4, "Handled req=%u, CID=%u, rc=%Rrc, cbData=%u, pvData=%p\n",
+#ifdef DEBUG_andy
+    VBoxServiceVerbose(4, "Handled req=%RU32, CID=%RU32, rc=%Rrc, cbData=%RU32, pvData=%p\n",
                        pRequest->enmType, pRequest->uCID, pRequest->rc,
                        pRequest->cbData, pRequest->pvData);
@@ -477,16 +422,19 @@
 static int gstcntlProcessHandleRequest(RTPOLLSET hPollSet, uint32_t fPollEvt,
                                        PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR,
-                                       PVBOXSERVICECTRLPROCESS pThread, PVBOXSERVICECTRLREQUEST pRequest)
+                                       PVBOXSERVICECTRLPROCESS pProcess, PVBOXSERVICECTRLREQUEST pRequest)
 {
     AssertPtrReturn(phStdInW, VERR_INVALID_POINTER);
     AssertPtrReturn(phStdOutR, VERR_INVALID_POINTER);
     AssertPtrReturn(phStdErrR, VERR_INVALID_POINTER);
-    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
+    AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
     AssertPtrReturn(pRequest, VERR_INVALID_POINTER);
+
+    VBoxServiceVerbose(4, "[PID %RU32]: Handling pRequest=%p\n",
+                       pProcess->uPID, pRequest);
 
     /* Drain the notification pipe. */
     uint8_t abBuf[8];
     size_t cbIgnore;
-    int rc = RTPipeRead(pThread->hNotificationPipeR, abBuf, sizeof(abBuf), &cbIgnore);
+    int rc = RTPipeRead(pProcess->hNotificationPipeR, abBuf, sizeof(abBuf), &cbIgnore);
     if (RT_FAILURE(rc))
         VBoxServiceError("Draining IPC notification pipe failed with rc=%Rrc\n", rc);
@@ -501,5 +449,5 @@
             /** @todo Check for some conditions to check to
              *        veto quitting. */
-            ASMAtomicXchgBool(&pThread->fShutdown, true);
+            ASMAtomicXchgBool(&pProcess->fShutdown, true);
             rcReq = VERR_CANCELLED;
             break;
@@ -572,5 +520,5 @@
 
         case VBOXSERVICECTRLREQUEST_PROC_TERM:
-            ASMAtomicXchgBool(&pThread->fShutdown, true);
+            ASMAtomicXchgBool(&pProcess->fShutdown, true);
             fDefer = true;
             break;
@@ -589,5 +537,5 @@
         /* No access to pRequest here anymore -- could be out of scope
          * or modified already! */
-        pThread->pRequest = pRequest = NULL;
+        pProcess->pRequest = pRequest = NULL;
     }
     else /* Completing the request defered. */
@@ -603,5 +551,5 @@
  *
  * @return  IPRT status code.
- * @param   pThread                     The process' thread handle.
+ * @param   pProcess                    The guest process to handle.
  * @param   hProcess                    The actual process handle.
  * @param   cMsTimeout                  Time limit (in ms) of the process' life time.
@@ -611,9 +559,9 @@
  * @param   hStdErrR                    Handle to the process' stderr read end.
  */
-static int gstcntlProcessProcLoop(PVBOXSERVICECTRLPROCESS pThread,
-                                  RTPROCESS hProcess, RTMSINTERVAL cMsTimeout, RTPOLLSET hPollSet,
+static int gstcntlProcessProcLoop(PVBOXSERVICECTRLPROCESS pProcess,
+                                  RTPROCESS hProcess, RTPOLLSET hPollSet,
                                   PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR)
 {
-    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
+    AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
     AssertPtrReturn(phStdInW, VERR_INVALID_PARAMETER);
     /* Rest is optional. */
@@ -621,5 +569,5 @@
     int                         rc;
     int                         rc2;
-    uint64_t const              MsStart             = RTTimeMilliTS();
+    uint64_t const              uMsStart            = RTTimeMilliTS();
     RTPROCSTATUS                ProcessStatus       = { 254, RTPROCEXITREASON_ABEND };
     bool                        fProcessAlive       = true;
@@ -636,5 +584,5 @@
      * the first (stale) entry will be found and we get really weird results!
      */
-    rc = gstcntlProcessAssignPID(pThread, hProcess);
+    rc = gstcntlProcessAssignPID(pProcess, hProcess);
     if (RT_FAILURE(rc))
     {
@@ -648,9 +596,10 @@
      * and that it's now OK to send input to the process.
      */
-    VBoxServiceVerbose(2, "[PID %u]: Process \"%s\" started, CID=%u, User=%s\n",
-                       pThread->uPID, pThread->pszCmd, pThread->uContextID, pThread->pszUser);
-    VBGLR3GUESTCTRLCMDCTX ctx = { pThread->uClientID, pThread->uContextID };
+    VBoxServiceVerbose(2, "[PID %RU32]: Process \"%s\" started, CID=%u, User=%s, cMsTimeout=%RU32\n",
+                       pProcess->uPID, pProcess->StartupInfo.szCmd, pProcess->uContextID,
+                       pProcess->StartupInfo.szUser, pProcess->StartupInfo.uTimeLimitMS);
+    VBGLR3GUESTCTRLCMDCTX ctx = { pProcess->uClientID, pProcess->uContextID };
     rc = VbglR3GuestCtrlProcCbStatus(&ctx,
-                                     pThread->uPID, PROC_STS_STARTED, 0 /* u32Flags */,
+                                     pProcess->uPID, PROC_STS_STARTED, 0 /* u32Flags */,
                                      NULL /* pvData */, 0 /* cbData */);
 
@@ -660,5 +609,5 @@
     PVBOXSERVICECTRLREQUEST pReq = NULL;
     while (   RT_SUCCESS(rc)
-           && RT_UNLIKELY(!pThread->fShutdown))
+           && RT_UNLIKELY(!pProcess->fShutdown))
     {
         /*
@@ -668,5 +617,5 @@
         uint32_t fPollEvt;
         rc2 = RTPollNoResume(hPollSet, cMsPollCur, &fPollEvt, &idPollHnd);
-        if (pThread->fShutdown)
+        if (pProcess->fShutdown)
             continue;
 
@@ -675,6 +624,4 @@
         if (RT_SUCCESS(rc2))
         {
-            /*VBoxServiceVerbose(4, "[PID %u}: RTPollNoResume idPollHnd=%u\n",
-                                 pThread->uPID, idPollHnd);*/
             switch (idPollHnd)
             {
@@ -693,8 +640,8 @@
 
                 case VBOXSERVICECTRLPIPEID_IPC_NOTIFY:
-                    pReq = pThread->pRequest; /** @todo Implement request queue. */
+                    pReq = pProcess->pRequest; /** @todo Implement request queue. */
                     rc = gstcntlProcessHandleRequest(hPollSet, fPollEvt,
                                                      phStdInW, phStdOutR, phStdErrR,
-                                                     pThread, pReq);
+                                                     pProcess, pReq);
                     if (rc != VINF_AIO_TASK_PENDING)
                         pReq = NULL;
@@ -708,15 +655,13 @@
             if (RT_FAILURE(rc) || rc == VINF_EOF)
                 break; /* Abort command, or client dead or something. */
-
-            if (RT_UNLIKELY(pThread->fShutdown))
-                break; /* We were asked to shutdown. */
-
-            continue;
-        }
-
-#if 0
-        VBoxServiceVerbose(4, "[PID %u]: Polling done, pollRC=%Rrc, pollCnt=%u, rc=%Rrc, fShutdown=%RTbool\n",
-                           pThread->uPID, rc2, RTPollSetGetCount(hPollSet), rc, pThread->fShutdown);
+        }
+#ifdef DEBUG_andy
+        VBoxServiceVerbose(4, "[PID %RU32]: Polling done, pollRc=%Rrc, pollCnt=%u, idPollHnd=%RU32, rc=%Rrc, fProcessAlive=%RTbool, fShutdown=%RTbool\n",
+                           pProcess->uPID, rc2, RTPollSetGetCount(hPollSet), idPollHnd, rc, fProcessAlive, pProcess->fShutdown);
 #endif
+
+        if (RT_UNLIKELY(pProcess->fShutdown))
+            break; /* We were asked to shutdown. */
+
         /*
          * Check for process death.
@@ -725,4 +670,10 @@
         {
             rc2 = RTProcWaitNoResume(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
+#ifdef DEBUG_andy
+            VBoxServiceVerbose(4, "[PID %RU32]: RTProcWaitNoResume=%Rrc, stdOut=%s, stdErrR=%s\n",
+                               pProcess->uPID, rc2,
+                               *phStdOutR == NIL_RTPIPE ? "closed" : "open",
+                               *phStdErrR == NIL_RTPIPE ? "closed" : "open");
+#endif
             if (RT_SUCCESS_NP(rc2))
             {
@@ -756,12 +707,12 @@
          */
         uint32_t cMilliesLeft = RT_INDEFINITE_WAIT;
-        if (cMsTimeout != RT_INDEFINITE_WAIT)
+        if (pProcess->StartupInfo.uTimeLimitMS != RT_INDEFINITE_WAIT)
         {
             uint64_t u64Now = RTTimeMilliTS();
-            uint64_t cMsElapsed = u64Now - MsStart;
-            if (cMsElapsed >= cMsTimeout)
+            uint64_t cMsElapsed = u64Now - uMsStart;
+            if (cMsElapsed >= pProcess->StartupInfo.uTimeLimitMS)
             {
-                VBoxServiceVerbose(3, "[PID %u]: Timed out (%ums elapsed > %ums timeout), killing ...\n",
-                                   pThread->uPID, cMsElapsed, cMsTimeout);
+                VBoxServiceVerbose(3, "[PID %RU32]: Timed out (%RU32ms elapsed > %RU32ms timeout), killing ...\n",
+                                   pProcess->uPID, cMsElapsed, pProcess->StartupInfo.uTimeLimitMS);
 
                 fProcessTimedOut = true;
@@ -778,5 +729,5 @@
             }
             else
-                cMilliesLeft = cMsTimeout - (uint32_t)cMsElapsed;
+                cMilliesLeft = pProcess->StartupInfo.uTimeLimitMS - (uint32_t)cMsElapsed;
         }
 
@@ -787,18 +738,12 @@
         if (cMilliesLeft < cMsPollCur)
             cMsPollCur = cMilliesLeft;
-
-        /*
-         * Need to exit?
-         */
-        if (pThread->fShutdown)
-            break;
-    }
-
-    rc2 = RTCritSectEnter(&pThread->CritSect);
+    }
+
+    rc2 = RTCritSectEnter(&pProcess->CritSect);
     if (RT_SUCCESS(rc2))
     {
-        ASMAtomicXchgBool(&pThread->fShutdown, true);
-
-        rc2 = RTCritSectLeave(&pThread->CritSect);
+        ASMAtomicXchgBool(&pProcess->fShutdown, true);
+
+        rc2 = RTCritSectLeave(&pProcess->CritSect);
         AssertRC(rc2);
     }
@@ -811,6 +756,6 @@
         if (MsProcessKilled == UINT64_MAX)
         {
-            VBoxServiceVerbose(3, "[PID %u]: Is still alive and not killed yet\n",
-                               pThread->uPID);
+            VBoxServiceVerbose(3, "[PID %RU32]: Is still alive and not killed yet\n",
+                               pProcess->uPID);
 
             MsProcessKilled = RTTimeMilliTS();
@@ -821,11 +766,11 @@
         for (size_t i = 0; i < 10; i++)
         {
-            VBoxServiceVerbose(4, "[PID %u]: Kill attempt %d/10: Waiting to exit ...\n",
-                               pThread->uPID, i + 1);
+            VBoxServiceVerbose(4, "[PID %RU32]: Kill attempt %d/10: Waiting to exit ...\n",
+                               pProcess->uPID, i + 1);
             rc2 = RTProcWait(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
             if (RT_SUCCESS(rc2))
             {
-                VBoxServiceVerbose(4, "[PID %u]: Kill attempt %d/10: Exited\n",
-                                   pThread->uPID, i + 1);
+                VBoxServiceVerbose(4, "[PID %RU32]: Kill attempt %d/10: Exited\n",
+                                   pProcess->uPID, i + 1);
                 fProcessAlive = false;
                 break;
@@ -833,6 +778,6 @@
             if (i >= 5)
             {
-                VBoxServiceVerbose(4, "[PID %u]: Kill attempt %d/10: Trying to terminate ...\n",
-                                   pThread->uPID, i + 1);
+                VBoxServiceVerbose(4, "[PID %RU32]: Kill attempt %d/10: Trying to terminate ...\n",
+                                   pProcess->uPID, i + 1);
                 RTProcTerminate(hProcess);
             }
@@ -841,5 +786,5 @@
 
         if (fProcessAlive)
-            VBoxServiceVerbose(3, "[PID %u]: Could not be killed\n", pThread->uPID);
+            VBoxServiceVerbose(3, "[PID %RU32]: Could not be killed\n", pProcess->uPID);
 
         if (   pReq /* Handle deferred termination request. */
@@ -865,35 +810,35 @@
         if (     fProcessTimedOut  && !fProcessAlive && MsProcessKilled != UINT64_MAX)
         {
-            VBoxServiceVerbose(3, "[PID %u]: Timed out and got killed\n",
-                               pThread->uPID);
+            VBoxServiceVerbose(3, "[PID %RU32]: Timed out and got killed\n",
+                               pProcess->uPID);
             uStatus = PROC_STS_TOK;
         }
         else if (fProcessTimedOut  &&  fProcessAlive && MsProcessKilled != UINT64_MAX)
         {
-            VBoxServiceVerbose(3, "[PID %u]: Timed out and did *not* get killed\n",
-                               pThread->uPID);
+            VBoxServiceVerbose(3, "[PID %RU32]: Timed out and did *not* get killed\n",
+                               pProcess->uPID);
             uStatus = PROC_STS_TOA;
         }
-        else if (pThread->fShutdown && (fProcessAlive || MsProcessKilled != UINT64_MAX))
-        {
-            VBoxServiceVerbose(3, "[PID %u]: Got terminated because system/service is about to shutdown\n",
-                               pThread->uPID);
+        else if (pProcess->fShutdown && (fProcessAlive || MsProcessKilled != UINT64_MAX))
+        {
+            VBoxServiceVerbose(3, "[PID %RU32]: Got terminated because system/service is about to shutdown\n",
+                               pProcess->uPID);
             uStatus = PROC_STS_DWN; /* Service is stopping, process was killed. */
-            uFlags = pThread->uFlags; /* Return handed-in execution flags back to the host. */
+            uFlags = pProcess->StartupInfo.uFlags; /* Return handed-in execution flags back to the host. */
         }
         else if (fProcessAlive)
         {
-            VBoxServiceError("[PID %u]: Is alive when it should not!\n",
-                             pThread->uPID);
+            VBoxServiceError("[PID %RU32]: Is alive when it should not!\n",
+                             pProcess->uPID);
         }
         else if (MsProcessKilled != UINT64_MAX)
         {
-            VBoxServiceError("[PID %u]: Has been killed when it should not!\n",
-                             pThread->uPID);
+            VBoxServiceError("[PID %RU32]: Has been killed when it should not!\n",
+                             pProcess->uPID);
         }
         else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL)
         {
-            VBoxServiceVerbose(3, "[PID %u]: Ended with RTPROCEXITREASON_NORMAL (Exit code: %u)\n",
-                               pThread->uPID, ProcessStatus.iStatus);
+            VBoxServiceVerbose(3, "[PID %RU32]: Ended with RTPROCEXITREASON_NORMAL (Exit code: %u)\n",
+                               pProcess->uPID, ProcessStatus.iStatus);
 
             uStatus = PROC_STS_TEN;
@@ -902,6 +847,6 @@
         else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL)
         {
-            VBoxServiceVerbose(3, "[PID %u]: Ended with RTPROCEXITREASON_SIGNAL (Signal: %u)\n",
-                               pThread->uPID, ProcessStatus.iStatus);
+            VBoxServiceVerbose(3, "[PID %RU32]: Ended with RTPROCEXITREASON_SIGNAL (Signal: %u)\n",
+                               pProcess->uPID, ProcessStatus.iStatus);
 
             uStatus = PROC_STS_TES;
@@ -911,6 +856,6 @@
         {
             /* ProcessStatus.iStatus will be undefined. */
-            VBoxServiceVerbose(3, "[PID %u]: Ended with RTPROCEXITREASON_ABEND\n",
-                               pThread->uPID);
+            VBoxServiceVerbose(3, "[PID %RU32]: Ended with RTPROCEXITREASON_ABEND\n",
+                               pProcess->uPID);
 
             uStatus = PROC_STS_TEA;
@@ -918,32 +863,32 @@
         }
         else
-            VBoxServiceVerbose(1, "[PID %u]: Handling process status %u not implemented\n",
-                               pThread->uPID, ProcessStatus.enmReason);
-
-        VBoxServiceVerbose(2, "[PID %u]: Ended, ClientID=%u, CID=%u, Status=%u, Flags=0x%x\n",
-                           pThread->uPID, pThread->uClientID, pThread->uContextID, uStatus, uFlags);
-
-        if (!(pThread->uFlags & EXECUTEPROCESSFLAG_WAIT_START))
-        {
-            VBGLR3GUESTCTRLCMDCTX ctx = { pThread->uClientID, pThread->uContextID };
+            VBoxServiceVerbose(1, "[PID %RU32]: Handling process status %u not implemented\n",
+                               pProcess->uPID, ProcessStatus.enmReason);
+
+        VBoxServiceVerbose(2, "[PID %RU32]: Ended, ClientID=%u, CID=%u, Status=%u, Flags=0x%x\n",
+                           pProcess->uPID, pProcess->uClientID, pProcess->uContextID, uStatus, uFlags);
+
+        if (!(pProcess->StartupInfo.uFlags & EXECUTEPROCESSFLAG_WAIT_START))
+        {
+            VBGLR3GUESTCTRLCMDCTX ctx = { pProcess->uClientID, pProcess->uContextID };
             rc2 = VbglR3GuestCtrlProcCbStatus(&ctx,
-                                              pThread->uPID, uStatus, uFlags,
+                                              pProcess->uPID, uStatus, uFlags,
                                               NULL /* pvData */, 0 /* cbData */);
             if (RT_FAILURE(rc2))
-                VBoxServiceError("[PID %u]: Error reporting final status to host; rc=%Rrc\n",
-                                 pThread->uPID, rc2);
+                VBoxServiceError("[PID %RU32]: Error reporting final status to host; rc=%Rrc\n",
+                                 pProcess->uPID, rc2);
             if (RT_SUCCESS(rc))
                 rc = rc2;
         }
         else
-            VBoxServiceVerbose(3, "[PID %u]: Was started detached, no final status sent to host\n",
-                               pThread->uPID);
-
-        VBoxServiceVerbose(3, "[PID %u]: Process loop ended with rc=%Rrc\n",
-                           pThread->uPID, rc);
+            VBoxServiceVerbose(3, "[PID %RU32]: Was started detached, no final status sent to host\n",
+                               pProcess->uPID);
+
+        VBoxServiceVerbose(3, "[PID %RU32]: Process loop ended with rc=%Rrc\n",
+                           pProcess->uPID, rc);
     }
     else
-        VBoxServiceError("[PID %u]: Loop failed with rc=%Rrc\n",
-                         pThread->uPID, rc);
+        VBoxServiceError("[PID %RU32]: Loop failed with rc=%Rrc\n",
+                         pProcess->uPID, rc);
     return rc;
 }
@@ -1034,9 +979,6 @@
 
 /**
- * Cancels a previously fired off guest thread request.
- *
- * Note: Does *not* do locking since GstCntlProcessRequestWait()
- * holds the lock (critsect); so only trigger the signal; the owner
- * needs to clean up afterwards.
+ * Cancels a previously fired off guest process request.
+ * Note: Caller is responsible for locking!
  *
  * @return  IPRT status code.
@@ -1357,5 +1299,5 @@
 
     AssertPtr(pThread->pSession);
-    int rc = RTCritSectEnter(&pThread->pSession->csControlThreads);
+    int rc = RTCritSectEnter(&pThread->pSession->CritSect);
     if (RT_SUCCESS(rc))
     {
@@ -1366,5 +1308,5 @@
         do
         {
-            RTListForEach(&pThread->pSession->lstControlThreadsActive, pThreadCur, VBOXSERVICECTRLPROCESS, Node)
+            RTListForEach(&pThread->pSession->lstProcessesActive, pThreadCur, VBOXSERVICECTRLPROCESS, Node)
             {
                 if (pThreadCur->uPID == uPID)
@@ -1384,5 +1326,5 @@
         pThread->uPID = uPID;
 
-        rc = RTCritSectLeave(&pThread->pSession->csControlThreads);
+        rc = RTCritSectLeave(&pThread->pSession->CritSect);
         AssertRC(rc);
     }
@@ -1562,5 +1504,5 @@
     AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
     VBoxServiceVerbose(3, "Thread of process pThread=0x%p = \"%s\" started\n",
-                       pProcess, pProcess->pszCmd);
+                       pProcess, pProcess->StartupInfo.szCmd);
 
     int rc = GstCntlSessionListSet(pProcess->pSession,
@@ -1576,7 +1518,55 @@
     }
     VBoxServiceVerbose(3, "Guest process \"%s\" got client ID=%u, flags=0x%x\n",
-                       pProcess->pszCmd, pProcess->uClientID, pProcess->uFlags);
+                       pProcess->StartupInfo.szCmd, pProcess->uClientID, pProcess->StartupInfo.uFlags);
 
     bool fSignalled = false; /* Indicator whether we signalled the thread user event already. */
+
+    /*
+     * Prepare argument list.
+     */
+    char **papszArgs;
+    uint32_t uNumArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */
+    rc = RTGetOptArgvFromString(&papszArgs, (int*)&uNumArgs,
+                                (pProcess->StartupInfo.uNumArgs > 0) ? pProcess->StartupInfo.szArgs : "", NULL);
+    /* Did we get the same result? */
+    Assert(pProcess->StartupInfo.uNumArgs == uNumArgs);
+
+    /*
+     * Prepare environment variables list.
+     */
+    char **papszEnv;
+    uint32_t uNumEnvVars = 0; /* Initialize in case of failing ... */
+    if (RT_SUCCESS(rc))
+    {
+        /* Prepare environment list. */
+        if (pProcess->StartupInfo.uNumEnvVars)
+        {
+            papszEnv = (char **)RTMemAlloc(pProcess->StartupInfo.uNumEnvVars * sizeof(char*));
+            AssertPtr(papszEnv);
+            uNumEnvVars = pProcess->StartupInfo.uNumEnvVars;
+
+            const char *pszCur = pProcess->StartupInfo.szEnv;
+            uint32_t i = 0;
+            uint32_t cbLen = 0;
+            while (cbLen < pProcess->StartupInfo.cbEnv)
+            {
+                /* sanity check */
+                if (i >= pProcess->StartupInfo.uNumEnvVars)
+                {
+                    rc = VERR_INVALID_PARAMETER;
+                    break;
+                }
+                int cbStr = RTStrAPrintf(&papszEnv[i++], "%s", pszCur);
+                if (cbStr < 0)
+                {
+                    rc = VERR_NO_STR_MEMORY;
+                    break;
+                }
+                pszCur += cbStr + 1; /* Skip terminating '\0' */
+                cbLen  += cbStr + 1; /* Skip terminating '\0' */
+            }
+            Assert(i == pProcess->StartupInfo.uNumEnvVars);
+        }
+    }
 
     /*
@@ -1588,7 +1578,7 @@
     {
         size_t i;
-        for (i = 0; i < pProcess->uNumEnvVars && pProcess->papszEnv; i++)
-        {
-            rc = RTEnvPutEx(hEnv, pProcess->papszEnv[i]);
+        for (i = 0; i < uNumEnvVars && papszEnv; i++)
+        {
+            rc = RTEnvPutEx(hEnv, papszEnv[i]);
             if (RT_FAILURE(rc))
                 break;
@@ -1609,5 +1599,5 @@
                 PRTHANDLE   phStdOut;
                 RTPIPE      pipeStdOutR;
-                rc = gstcntlProcessSetupPipe(  (pProcess->uFlags & EXECUTEPROCESSFLAG_WAIT_STDOUT)
+                rc = gstcntlProcessSetupPipe(  (pProcess->StartupInfo.uFlags & EXECUTEPROCESSFLAG_WAIT_STDOUT)
                                              ? "|" : "/dev/null",
                                              1 /*STDOUT_FILENO*/,
@@ -1618,5 +1608,5 @@
                     PRTHANDLE   phStdErr;
                     RTPIPE      pipeStdErrR;
-                    rc = gstcntlProcessSetupPipe(  (pProcess->uFlags & EXECUTEPROCESSFLAG_WAIT_STDERR)
+                    rc = gstcntlProcessSetupPipe(  (pProcess->StartupInfo.uFlags & EXECUTEPROCESSFLAG_WAIT_STDERR)
                                                  ? "|" : "/dev/null",
                                                  2 /*STDERR_FILENO*/,
@@ -1658,8 +1648,8 @@
 
                                 RTPROCESS hProcess;
-                                rc = gstcntlProcessCreateProcess(pProcess->pszCmd, pProcess->papszArgs, hEnv, pProcess->uFlags,
+                                rc = gstcntlProcessCreateProcess(pProcess->StartupInfo.szCmd, papszArgs, hEnv, pProcess->StartupInfo.uFlags,
                                                                  phStdIn, phStdOut, phStdErr,
-                                                                 fNeedsImpersonation ? pProcess->pszUser : NULL,
-                                                                 fNeedsImpersonation ? pProcess->pszPassword : NULL,
+                                                                 fNeedsImpersonation ? pProcess->StartupInfo.szUser : NULL,
+                                                                 fNeedsImpersonation ? pProcess->StartupInfo.szPassword : NULL,
                                                                  &hProcess);
                                 if (RT_FAILURE(rc))
@@ -1689,6 +1679,5 @@
 
                                     /* Enter the process loop. */
-                                    rc = gstcntlProcessProcLoop(pProcess,
-                                                                hProcess, pProcess->uTimeLimitMS, hPollSet,
+                                    rc = gstcntlProcessProcLoop(pProcess, hProcess, hPollSet,
                                                                 &pProcess->pipeStdInW, &pipeStdOutR, &pipeStdErrR);
 
@@ -1759,5 +1748,5 @@
         /* Disconnect this client from the guest control service. This also cancels all
          * outstanding host requests. */
-        VBoxServiceVerbose(3, "[PID %u]: Disconnecting (client ID=%u) ...\n",
+        VBoxServiceVerbose(3, "[PID %RU32]: Disconnecting (client ID=%u) ...\n",
                            pProcess->uPID, pProcess->uClientID);
         VbglR3GuestCtrlDisconnect(pProcess->uClientID);
@@ -1765,6 +1754,16 @@
     }
 
-    VBoxServiceVerbose(3, "[PID %u]: Thread of process \"%s\" ended with rc=%Rrc\n",
-                       pProcess->uPID, pProcess->pszCmd, rc);
+    VBoxServiceVerbose(3, "[PID %RU32]: Thread of process \"%s\" ended with rc=%Rrc\n",
+                       pProcess->uPID, pProcess->StartupInfo.szCmd, rc);
+
+    /* Free argument + environment variable lists. */
+    if (uNumEnvVars)
+    {
+        for (uint32_t i = 0; i < uNumEnvVars; i++)
+            RTStrFree(papszEnv[i]);
+        RTMemFree(papszEnv);
+    }
+    if (uNumArgs)
+        RTGetOptArgvFree(papszArgs);
 
     /* Update started/stopped status. */
@@ -1779,4 +1778,6 @@
         RTThreadUserSignal(RTThreadSelf());
 
+    VBoxServiceVerbose(3, "[PID %RU32]: Thread returned with rc=%Rrc\n",
+                       pProcess->uPID, rc);
     return rc;
 }
@@ -1867,5 +1868,5 @@
  * Performs a request to a specific (formerly started) guest process and waits
  * for its response.
- * Note: Caller is responsible of locking!
+ * Note: Caller is responsible for locking!
  *
  * @return  IPRT status code.
@@ -1889,4 +1890,7 @@
     else
     {
+        VBoxServiceVerbose(3, "[PID %RU32]: Sending pRequest=%p\n",
+                           pProcess->uPID, pRequest);
+
         /* Set request structure pointer. */
         pProcess->pRequest = pRequest;
@@ -1894,16 +1898,16 @@
         /** @todo To speed up simultaneous guest process handling we could add a worker threads
          *        or queue in order to wait for the request to happen. Later. */
-        /* Wake up guest thrad by sending a wakeup byte to the notification pipe so
+        /* Wake up guest thread by sending a wakeup byte to the notification pipe so
          * that RTPoll unblocks (returns) and we then can do our requested operation. */
         Assert(pProcess->hNotificationPipeW != NIL_RTPIPE);
-        size_t cbWritten;
+        size_t cbWritten = 0;
         if (RT_SUCCESS(rc))
             rc = RTPipeWrite(pProcess->hNotificationPipeW, "i", 1, &cbWritten);
 
-        if (   RT_SUCCESS(rc)
-            && cbWritten)
-        {
-            VBoxServiceVerbose(3, "[PID %u]: Waiting for response on enmType=%u, pvData=0x%p, cbData=%u\n",
-                               pProcess->uPID, pRequest->enmType, pRequest->pvData, pRequest->cbData);
+        if (RT_SUCCESS(rc))
+        {
+            Assert(cbWritten);
+            VBoxServiceVerbose(3, "[PID %RU32]: Waiting for response on pRequest=%p, enmType=%u, pvData=0x%p, cbData=%u\n",
+                               pProcess->uPID, pRequest, pRequest->enmType, pRequest->pvData, pRequest->cbData);
 
             rc = GstCntlProcessRequestWait(pRequest);
@@ -1911,6 +1915,6 @@
     }
 
-    VBoxServiceVerbose(3, "[PID %u]: Performed enmType=%u, uCID=%u, pvData=0x%p, cbData=%u, rc=%Rrc\n",
-                       pProcess->uPID, pRequest->enmType, pRequest->uCID, pRequest->pvData, pRequest->cbData, rc);
+    VBoxServiceVerbose(3, "[PID %RU32]: Performed pRequest=%p, enmType=%u, uCID=%u, pvData=0x%p, cbData=%u, rc=%Rrc\n",
+                       pProcess->uPID, pRequest, pRequest->enmType, pRequest->uCID, pRequest->pvData, pRequest->cbData, rc);
     return rc;
 }
Index: /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlSession.cpp
===================================================================
--- /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlSession.cpp	(revision 45414)
+++ /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlSession.cpp	(revision 45415)
@@ -70,5 +70,6 @@
     VBOXSERVICESESSIONOPT_USERNAME,
     VBOXSERVICESESSIONOPT_SESSION_ID,
-    VBOXSERVICESESSIONOPT_SESSION_PROTO
+    VBOXSERVICESESSIONOPT_SESSION_PROTO,
+    VBOXSERVICESESSIONOPT_THREAD_ID
 };
 
@@ -568,19 +569,13 @@
         if (RT_SUCCESS(rc))
         {
-            VBoxServiceVerbose(3, "Request to start process szCmd=%s, uFlags=0x%x, szArgs=%s, szEnv=%s, szUser=%s, szPassword=%s, uTimeout=%RU32\n",
+            VBoxServiceVerbose(3, "Request to start process szCmd=%s, uFlags=0x%x, szArgs=%s, szEnv=%s, uTimeout=%RU32\n",
                                startupInfo.szCmd, startupInfo.uFlags,
                                startupInfo.uNumArgs ? startupInfo.szArgs : "<None>",
                                startupInfo.uNumEnvVars ? startupInfo.szEnv : "<None>",
-                               startupInfo.szUser,
-#ifdef DEBUG
-                               startupInfo.szPassword,
-#else
-                               "XXX", /* Never show passwords in release mode. */
-#endif
                                startupInfo.uTimeLimitMS);
 
-            rc = GstCntlSessionReapProcesses(pSession);
+            /*rc = GstCntlSessionReapProcesses(pSession);
             if (RT_FAILURE(rc))
-                VBoxServiceError("Reaping stopped guest processes failed with rc=%Rrc\n", rc);
+                VBoxServiceError("Reaping stopped guest processes failed with rc=%Rrc\n", rc);*/
             /* Keep going. */
 
@@ -741,4 +736,8 @@
 
     int rc = VbglR3GuestCtrlProcGetOutput(pHostCtx, &uPID, &uHandleID, &uFlags);
+#ifdef DEBUG_andy
+    VBoxServiceVerbose(4, "[PID %RU32]: Get output CID=%RU32, uHandleID=%RU32, uFlags=%RU32\n",
+                       uPID, pHostCtx->uContextID, uHandleID, uFlags);
+#endif
     if (RT_SUCCESS(rc))
     {
@@ -750,5 +749,5 @@
                                          pHostCtx->uContextID, uHandleID, RT_INDEFINITE_WAIT /* Timeout */,
                                          pBuf, _64K /* cbSize */, &cbRead);
-            VBoxServiceVerbose(3, "[PID %RU32]: Got output, rc=%Rrc, CID=%u, cbRead=%u, uHandle=%u, uFlags=%u\n",
+            VBoxServiceVerbose(3, "[PID %RU32]: Got output, rc=%Rrc, CID=%RU32, cbRead=%RU32, uHandle=%RU32, uFlags=%x\n",
                                uPID, rc, pHostCtx->uContextID, cbRead, uHandleID, uFlags);
 
@@ -973,7 +972,7 @@
 
         default:
-            VBoxServiceVerbose(3, "Unsupported message from host, uMsg=%RU32, cParms=%RU32\n",
+            rc = VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID);
+            VBoxServiceVerbose(3, "Unsupported message (uMsg=%RU32, cParms=%RU32) from host, skipping\n",
                                uMsg, pHostCtx->uNumParms);
-            /* Don't terminate here; just wait for the next message. */
             break;
     }
@@ -1008,10 +1007,13 @@
     }
     else
+    {
         VBoxServiceError("Error connecting to guest control service, rc=%Rrc\n", rc);
+        return rc;
+    }
 
     /* Let caller know that we're done initializing. */
-    int rc2 = RTThreadUserSignal(RTThreadSelf());
-    if (RT_SUCCESS(rc))
-        rc = rc2;
+    rc = RTThreadUserSignal(RTThreadSelf());
+    if (RT_FAILURE(rc))
+        return rc;
 
     bool fProcessAlive = true;
@@ -1019,4 +1021,5 @@
     RT_ZERO(ProcessStatus);
 
+    int rcWait;
     if (RT_SUCCESS(rc))
     {
@@ -1024,5 +1027,4 @@
         uint64_t u64TimeoutStart = 0;
 
-        int rcWait;
         for (;;)
         {
@@ -1048,4 +1050,5 @@
                                        uSessionID);
                     u64TimeoutStart = RTTimeMilliTS();
+                    continue; /* Don't waste time on waiting. */
                 }
                 if (RTTimeMilliTS() - u64TimeoutStart > uTimeoutsMS)
@@ -1084,5 +1087,5 @@
         }
 
-        VBoxServiceVerbose(2, "Guest session ID=%RU32 process has been killed with rc=%Rc\n",
+        VBoxServiceVerbose(2, "Guest session ID=%RU32 process termination resulted in rc=%Rrc\n",
                            uSessionID, rc);
 
@@ -1092,27 +1095,36 @@
     else
     {
-        switch (ProcessStatus.enmReason)
-        {
-            case RTPROCEXITREASON_NORMAL:
-                uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEN;
-                break;
-
-            case RTPROCEXITREASON_ABEND:
-                uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEA;
-                break;
-
-            case RTPROCEXITREASON_SIGNAL:
-                uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TES;
-                break;
-
-            default:
-                AssertMsgFailed(("Unhandled process termination reason (%ld)",
-                                 ProcessStatus.enmReason));
-                uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEA;
-                break;
-        }
-    }
-
-    VBoxServiceVerbose(3, "Guest session ID=%RU32 thread ended with sessionStatus=%ld, sessionRc=%Rrc\n",
+        if (RT_SUCCESS(rcWait))
+        {
+            switch (ProcessStatus.enmReason)
+            {
+                case RTPROCEXITREASON_NORMAL:
+                    uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEN;
+                    break;
+
+                case RTPROCEXITREASON_ABEND:
+                    uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEA;
+                    break;
+
+                case RTPROCEXITREASON_SIGNAL:
+                    uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TES;
+                    break;
+
+                default:
+                    AssertMsgFailed(("Unhandled process termination reason (%ld)\n",
+                                     ProcessStatus.enmReason));
+                    uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEA;
+                    break;
+            }
+        }
+        else
+        {
+            /* If we didn't find the guest process anymore, just assume it
+             * terminated normally. */
+            uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEN;
+        }
+    }
+
+    VBoxServiceVerbose(3, "Guest session ID=%RU32 thread ended with sessionStatus=%RU32, sessionRc=%Rrc\n",
                        uSessionID, uSessionStatus, uSessionRc);
 
@@ -1120,6 +1132,6 @@
     Assert(uSessionStatus != GUEST_SESSION_NOTIFYTYPE_UNDEFINED);
     VBGLR3GUESTCTRLCMDCTX ctx = { uClientID, VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(uSessionID) };
-    rc2 = VbglR3GuestCtrlSessionNotify(&ctx,
-                                       uSessionStatus, uSessionRc);
+    int rc2 = VbglR3GuestCtrlSessionNotify(&ctx,
+                                           uSessionStatus, uSessionRc);
     if (RT_FAILURE(rc2))
         VBoxServiceError("Reporting session ID=%RU32 final status failed with rc=%Rrc\n",
@@ -1254,26 +1266,26 @@
     AssertPtrReturn(pSession, NULL);
 
-    PVBOXSERVICECTRLPROCESS pThread = NULL;
-    int rc = RTCritSectEnter(&pSession->csControlThreads);
-    if (RT_SUCCESS(rc))
-    {
-        PVBOXSERVICECTRLPROCESS pThreadCur;
-        RTListForEach(&pSession->lstControlThreadsActive, pThreadCur, VBOXSERVICECTRLPROCESS, Node)
-        {
-            if (pThreadCur->uPID == uPID)
-            {
-                rc = RTCritSectEnter(&pThreadCur->CritSect);
+    PVBOXSERVICECTRLPROCESS pProcess = NULL;
+    int rc = RTCritSectEnter(&pSession->CritSect);
+    if (RT_SUCCESS(rc))
+    {
+        PVBOXSERVICECTRLPROCESS pCurProcess;
+        RTListForEach(&pSession->lstProcessesActive, pCurProcess, VBOXSERVICECTRLPROCESS, Node)
+        {
+            if (pCurProcess->uPID == uPID)
+            {
+                rc = RTCritSectEnter(&pCurProcess->CritSect);
                 if (RT_SUCCESS(rc))
-                    pThread = pThreadCur;
+                    pProcess = pCurProcess;
                 break;
             }
         }
 
-        int rc2 = RTCritSectLeave(&pSession->csControlThreads);
+        int rc2 = RTCritSectLeave(&pSession->CritSect);
         if (RT_SUCCESS(rc))
             rc = rc2;
     }
 
-    return pThread;
+    return pProcess;
 }
 
@@ -1286,70 +1298,93 @@
                        pSession->StartupInfo.uSessionID);
 
-    /*
-     * Close all guest processes.
-     */
-
-    /* Signal all guest processes in the active list that we want to shutdown. */
-    PVBOXSERVICECTRLPROCESS pProcess;
-    RTListForEach(&pSession->lstControlThreadsActive, pProcess, VBOXSERVICECTRLPROCESS, Node)
-        GstCntlProcessStop(pProcess);
-
-    /* Wait for all active threads to shutdown and destroy the active thread list. */
-    pProcess = RTListGetFirst(&pSession->lstControlThreadsActive, VBOXSERVICECTRLPROCESS, Node);
-    while (pProcess)
-    {
-        PVBOXSERVICECTRLPROCESS pNext = RTListNodeGetNext(&pProcess->Node, VBOXSERVICECTRLPROCESS, Node);
-        bool fLast = RTListNodeIsLast(&pSession->lstControlThreadsActive, &pProcess->Node);
-
-        int rc2 = GstCntlProcessWait(pProcess,
-                                     30 * 1000 /* Wait 30 seconds max. */,
-                                     NULL /* rc */);
-        if (RT_FAILURE(rc2))
-        {
-            VBoxServiceError("Guest process thread failed to stop; rc=%Rrc\n", rc2);
-            /* Keep going. */
-        }
-
-        if (fLast)
-            break;
-
-        pProcess = pNext;
-    }
-
-    int rc = GstCntlSessionReapProcesses(pSession);
-    if (RT_FAILURE(rc))
-        VBoxServiceError("Reaping inactive threads failed with rc=%Rrc\n", rc);
-
-    AssertMsg(RTListIsEmpty(&pSession->lstControlThreadsActive),
-              ("Guest process active thread list still contains entries when it should not\n"));
-    AssertMsg(RTListIsEmpty(&pSession->lstControlThreadsInactive),
-              ("Guest process inactive thread list still contains entries when it should not\n"));
-
-    /*
-     * Close all left guest files.
-     */
-    PVBOXSERVICECTRLFILE pFile;
-    pFile = RTListGetFirst(&pSession->lstFiles, VBOXSERVICECTRLFILE, Node);
-    while (pFile)
-    {
-        PVBOXSERVICECTRLFILE pNext = RTListNodeGetNext(&pFile->Node, VBOXSERVICECTRLFILE, Node);
-        bool fLast = RTListNodeIsLast(&pSession->lstFiles, &pFile->Node);
-
-        int rc2 = gstcntlSessionFileDestroy(pFile);
-        if (RT_FAILURE(rc2))
-        {
-            VBoxServiceError("Unable to close file \"%s\"; rc=%Rrc\n",
-                             pFile->szName, rc2);
-            /* Keep going. */
-        }
-
-        if (fLast)
-            break;
-
-        pFile = pNext;
-    }
-
-    AssertMsg(RTListIsEmpty(&pSession->lstFiles),
-              ("Guest file list still contains entries when it should not\n"));
+    int rc = RTCritSectEnter(&pSession->CritSect);
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Close all guest processes.
+         */
+
+        /* Signal all guest processes in the active list that we want to shutdown. */
+        PVBOXSERVICECTRLPROCESS pProcess;
+        RTListForEach(&pSession->lstProcessesActive, pProcess, VBOXSERVICECTRLPROCESS, Node)
+            GstCntlProcessStop(pProcess);
+
+        /* Wait for all active threads to shutdown and destroy the active thread list. */
+        pProcess = RTListGetFirst(&pSession->lstProcessesActive, VBOXSERVICECTRLPROCESS, Node);
+        while (pProcess)
+        {
+            PVBOXSERVICECTRLPROCESS pNext = RTListNodeGetNext(&pProcess->Node, VBOXSERVICECTRLPROCESS, Node);
+            bool fLast = RTListNodeIsLast(&pSession->lstProcessesActive, &pProcess->Node);
+
+            int rc2 = GstCntlProcessWait(pProcess,
+                                         30 * 1000 /* Wait 30 seconds max. */,
+                                         NULL /* rc */);
+            if (RT_FAILURE(rc2))
+            {
+                VBoxServiceError("Guest process thread failed to stop; rc=%Rrc\n", rc2);
+                if (RT_SUCCESS(rc))
+                    rc = rc2;
+                /* Keep going. */
+            }
+
+            rc2 = GstCntlProcessFree(pProcess);
+            if (RT_FAILURE(rc2))
+            {
+                VBoxServiceError("Guest process thread failed to free; rc=%Rrc\n", rc2);
+                if (RT_SUCCESS(rc))
+                    rc = rc2;
+                /* Keep going. */
+            }
+
+            RTListNodeRemove(&pProcess->Node);
+
+            if (fLast)
+                break;
+
+            pProcess = pNext;
+        }
+
+        /*rc = GstCntlSessionReapProcesses(pSession);
+        if (RT_FAILURE(rc))
+            VBoxServiceError("Reaping inactive threads failed with rc=%Rrc\n", rc);*/
+
+        AssertMsg(RTListIsEmpty(&pSession->lstProcessesActive),
+                  ("Guest process active thread list still contains entries when it should not\n"));
+        /*AssertMsg(RTListIsEmpty(&pSession->lstProcessesInactive),
+                  ("Guest process inactive thread list still contains entries when it should not\n"));*/
+
+        /*
+         * Close all left guest files.
+         */
+        PVBOXSERVICECTRLFILE pFile;
+        pFile = RTListGetFirst(&pSession->lstFiles, VBOXSERVICECTRLFILE, Node);
+        while (pFile)
+        {
+            PVBOXSERVICECTRLFILE pNext = RTListNodeGetNext(&pFile->Node, VBOXSERVICECTRLFILE, Node);
+            bool fLast = RTListNodeIsLast(&pSession->lstFiles, &pFile->Node);
+
+            int rc2 = gstcntlSessionFileDestroy(pFile);
+            if (RT_FAILURE(rc2))
+            {
+                VBoxServiceError("Unable to close file \"%s\"; rc=%Rrc\n",
+                                 pFile->szName, rc2);
+                if (RT_SUCCESS(rc))
+                    rc = rc2;
+                /* Keep going. */
+            }
+
+            if (fLast)
+                break;
+
+            pFile = pNext;
+        }
+
+        AssertMsg(RTListIsEmpty(&pSession->lstFiles),
+                  ("Guest file list still contains entries when it should not\n"));
+
+        int rc2 = RTCritSectLeave(&pSession->CritSect);
+        if (RT_SUCCESS(rc))
+            rc = rc2;
+    }
 
     return rc;
@@ -1364,5 +1399,5 @@
 
     /* Destroy critical section. */
-    RTCritSectDelete(&pSession->csControlThreads);
+    RTCritSectDelete(&pSession->CritSect);
 
     return rc;
@@ -1441,6 +1476,6 @@
     AssertPtrReturn(pSession, VERR_INVALID_POINTER);
 
-    RTListInit(&pSession->lstControlThreadsActive);
-    RTListInit(&pSession->lstControlThreadsInactive);
+    RTListInit(&pSession->lstProcessesActive);
+    RTListInit(&pSession->lstProcessesInactive);
     RTListInit(&pSession->lstFiles);
 
@@ -1457,5 +1492,5 @@
 
     /* Init critical section for protecting the thread lists. */
-    int rc = RTCritSectInit(&pSession->csControlThreads);
+    int rc = RTCritSectInit(&pSession->CritSect);
     AssertRC(rc);
 
@@ -1481,5 +1516,5 @@
     AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
 
-    int rc = RTCritSectEnter(&pSession->csControlThreads);
+    int rc = RTCritSectEnter(&pSession->CritSect);
     if (RT_SUCCESS(rc))
     {
@@ -1491,13 +1526,14 @@
         {
             case VBOXSERVICECTRLTHREADLIST_STOPPED:
-                pAnchor = &pSession->lstControlThreadsInactive;
+                pAnchor = &pSession->lstProcessesInactive;
                 break;
 
             case VBOXSERVICECTRLTHREADLIST_RUNNING:
-                pAnchor = &pSession->lstControlThreadsActive;
+                pAnchor = &pSession->lstProcessesActive;
                 break;
 
             default:
-                AssertMsgFailed(("Unknown list type: %u", enmList));
+                AssertMsgFailed(("Unknown list type: %u\n",
+                                 enmList));
                 break;
         }
@@ -1520,5 +1556,5 @@
         }
 
-        int rc2 = RTCritSectLeave(&pSession->csControlThreads);
+        int rc2 = RTCritSectLeave(&pSession->CritSect);
         if (RT_SUCCESS(rc))
             rc = rc2;
@@ -1546,5 +1582,5 @@
     AssertPtrReturn(pbAllowed, VERR_INVALID_POINTER);
 
-    int rc = RTCritSectEnter(&pSession->csControlThreads);
+    int rc = RTCritSectEnter(&pSession->CritSect);
     if (RT_SUCCESS(rc))
     {
@@ -1557,6 +1593,6 @@
         {
             uint32_t uProcsRunning = 0;
-            PVBOXSERVICECTRLPROCESS pThread;
-            RTListForEach(&pSession->lstControlThreadsActive, pThread, VBOXSERVICECTRLPROCESS, Node)
+            PVBOXSERVICECTRLPROCESS pProcess;
+            RTListForEach(&pSession->lstProcessesActive, pProcess, VBOXSERVICECTRLPROCESS, Node)
                 uProcsRunning++;
 
@@ -1575,5 +1611,5 @@
         *pbAllowed = !fLimitReached;
 
-        int rc2 = RTCritSectLeave(&pSession->csControlThreads);
+        int rc2 = RTCritSectLeave(&pSession->CritSect);
         if (RT_SUCCESS(rc))
             rc = rc2;
@@ -1583,7 +1619,8 @@
 }
 
-
+#if 0
 /**
  * Reaps all inactive guest process threads.
+ * Does not do locking; this is the job of the caller.
  *
  * @return  IPRT status code.
@@ -1593,40 +1630,32 @@
     AssertPtrReturn(pSession, VERR_INVALID_POINTER);
 
-    int rc = RTCritSectEnter(&pSession->csControlThreads);
-    if (RT_SUCCESS(rc))
-    {
-        PVBOXSERVICECTRLPROCESS pThread =
-            RTListGetFirst(&pSession->lstControlThreadsInactive, VBOXSERVICECTRLPROCESS, Node);
-        while (pThread)
-        {
-            PVBOXSERVICECTRLPROCESS pNext = RTListNodeGetNext(&pThread->Node, VBOXSERVICECTRLPROCESS, Node);
-            bool fLast = RTListNodeIsLast(&pSession->lstControlThreadsInactive, &pThread->Node);
-            int rc2 = GstCntlProcessWait(pThread, 30 * 1000 /* 30 seconds max. */,
-                                         NULL /* rc */);
-            if (RT_SUCCESS(rc2))
-            {
-                RTListNodeRemove(&pThread->Node);
-
-                rc2 = GstCntlProcessFree(pThread);
-                if (RT_FAILURE(rc2))
-                {
-                    VBoxServiceError("Freeing guest process thread failed with rc=%Rrc\n", rc2);
-                    if (RT_SUCCESS(rc)) /* Keep original failure. */
-                        rc = rc2;
-                }
-            }
-            else
-                VBoxServiceError("Waiting on guest process thread failed with rc=%Rrc\n", rc2);
-            /* Keep going. */
-
-            if (fLast)
-                break;
-
-            pThread = pNext;
-        }
-
-        int rc2 = RTCritSectLeave(&pSession->csControlThreads);
-        if (RT_SUCCESS(rc))
-            rc = rc2;
+    PVBOXSERVICECTRLPROCESS pThread =
+        RTListGetFirst(&pSession->lstProcessesInactive, VBOXSERVICECTRLPROCESS, Node);
+    while (pThread)
+    {
+        PVBOXSERVICECTRLPROCESS pNext = RTListNodeGetNext(&pThread->Node, VBOXSERVICECTRLPROCESS, Node);
+        bool fLast = RTListNodeIsLast(&pSession->lstProcessesInactive, &pThread->Node);
+        int rc2 = GstCntlProcessWait(pThread, 30 * 1000 /* 30 seconds max. */,
+                                     NULL /* rc */);
+        if (RT_SUCCESS(rc2))
+        {
+            RTListNodeRemove(&pThread->Node);
+
+            rc2 = GstCntlProcessFree(pThread);
+            if (RT_FAILURE(rc2))
+            {
+                VBoxServiceError("Freeing guest process thread failed with rc=%Rrc\n", rc2);
+                if (RT_SUCCESS(rc)) /* Keep original failure. */
+                    rc = rc2;
+            }
+        }
+        else
+            VBoxServiceError("Waiting on guest process thread failed with rc=%Rrc\n", rc2);
+        /* Keep going. */
+
+        if (fLast)
+            break;
+
+        pThread = pNext;
     }
 
@@ -1634,4 +1663,5 @@
     return rc;
 }
+#endif
 
 
@@ -1700,7 +1730,7 @@
  *                                  Optional.
  */
-int GstCntlSessionThreadOpen(PRTLISTANCHOR pList,
-                             const PVBOXSERVICECTRLSESSIONSTARTUPINFO pSessionStartupInfo,
-                             PVBOXSERVICECTRLSESSIONTHREAD *ppSessionThread)
+int GstCntlSessionThreadCreate(PRTLISTANCHOR pList,
+                               const PVBOXSERVICECTRLSESSIONSTARTUPINFO pSessionStartupInfo,
+                               PVBOXSERVICECTRLSESSIONTHREAD *ppSessionThread)
 {
     AssertPtrReturn(pList, VERR_INVALID_POINTER);
@@ -1716,5 +1746,5 @@
         if (pSessionCur->StartupInfo.uSessionID == pSessionStartupInfo->uSessionID)
         {
-            AssertMsgFailed(("Guest session %RU32 (%p) already exists when it should not",
+            AssertMsgFailed(("Guest session thread ID=%RU32 (%p) already exists when it should not\n",
                              pSessionCur->StartupInfo.uSessionID, pSessionCur));
             return VERR_ALREADY_EXISTS;
@@ -1724,21 +1754,28 @@
     int rc = VINF_SUCCESS;
 
-    PVBOXSERVICECTRLSESSIONTHREAD pSession = (PVBOXSERVICECTRLSESSIONTHREAD)RTMemAllocZ(sizeof(VBOXSERVICECTRLSESSIONTHREAD));
-    if (pSession)
+    /* Static counter to help tracking session thread <-> process relations. */
+    static uint32_t s_uCtrlSessionThread = 0;
+    if (s_uCtrlSessionThread++ == UINT32_MAX)
+        s_uCtrlSessionThread = 0; /* Wrap around to not let IPRT freak out. */
+
+    PVBOXSERVICECTRLSESSIONTHREAD pSessionThread =
+        (PVBOXSERVICECTRLSESSIONTHREAD)RTMemAllocZ(sizeof(VBOXSERVICECTRLSESSIONTHREAD));
+    if (pSessionThread)
     {
         /* Copy over session startup info. */
-        memcpy(&pSession->StartupInfo, pSessionStartupInfo, sizeof(VBOXSERVICECTRLSESSIONSTARTUPINFO));
-
-        pSession->fShutdown = false;
-        pSession->fStarted  = false;
-        pSession->fStopped  = false;
+        memcpy(&pSessionThread->StartupInfo, pSessionStartupInfo,
+               sizeof(VBOXSERVICECTRLSESSIONSTARTUPINFO));
+
+        pSessionThread->fShutdown = false;
+        pSessionThread->fStarted  = false;
+        pSessionThread->fStopped  = false;
 
         /* Is this an anonymous session? */
         /* Anonymous sessions run with the same privileges as the main VBoxService executable. */
-        bool fAnonymous = !RT_BOOL(strlen(pSession->StartupInfo.szUser));
+        bool fAnonymous = !RT_BOOL(strlen(pSessionThread->StartupInfo.szUser));
         if (fAnonymous)
         {
-            Assert(!strlen(pSession->StartupInfo.szPassword));
-            Assert(!strlen(pSession->StartupInfo.szDomain));
+            Assert(!strlen(pSessionThread->StartupInfo.szPassword));
+            Assert(!strlen(pSessionThread->StartupInfo.szDomain));
 
             VBoxServiceVerbose(3, "New anonymous guest session ID=%RU32 created, uFlags=%x, using protocol %RU32\n",
@@ -1762,5 +1799,5 @@
         }
 
-        rc = RTCritSectInit(&pSession->CritSect);
+        rc = RTCritSectInit(&pSessionThread->CritSect);
         AssertRC(rc);
 
@@ -1773,10 +1810,10 @@
             if (!fAnonymous)
             {
-                if (!RTStrPrintf(szParmUserName, sizeof(szParmUserName), "--username=%s", pSession->StartupInfo.szUser))
+                if (!RTStrPrintf(szParmUserName, sizeof(szParmUserName), "--user=%s", pSessionThread->StartupInfo.szUser))
                     rc = VERR_BUFFER_OVERFLOW;
             }
             char szParmSessionID[32];
             if (RT_SUCCESS(rc) && !RTStrPrintf(szParmSessionID, sizeof(szParmSessionID), "--session-id=%RU32",
-                                               pSession->StartupInfo.uSessionID))
+                                               pSessionThread->StartupInfo.uSessionID))
             {
                 rc = VERR_BUFFER_OVERFLOW;
@@ -1784,18 +1821,28 @@
             char szParmSessionProto[32];
             if (RT_SUCCESS(rc) && !RTStrPrintf(szParmSessionProto, sizeof(szParmSessionProto), "--session-proto=%RU32",
-                                               pSession->StartupInfo.uProtocol))
+                                               pSessionThread->StartupInfo.uProtocol))
             {
                 rc = VERR_BUFFER_OVERFLOW;
             }
-
+#ifdef DEBUG
+            char szParmThreadId[32];
+            if (RT_SUCCESS(rc) && !RTStrPrintf(szParmThreadId, sizeof(szParmThreadId), "--thread-id=%RU32",
+                                               s_uCtrlSessionThread))
+            {
+                rc = VERR_BUFFER_OVERFLOW;
+            }
+#endif /* DEBUG */
             if (RT_SUCCESS(rc))
             {
                 int iOptIdx = 0; /* Current index in argument vector. */
 
-                char const *papszArgs[8];
+                char const *papszArgs[16];
                 papszArgs[iOptIdx++] = pszExeName;
                 papszArgs[iOptIdx++] = "guestsession";
                 papszArgs[iOptIdx++] = szParmSessionID;
                 papszArgs[iOptIdx++] = szParmSessionProto;
+#ifdef DEBUG
+                papszArgs[iOptIdx++] = szParmThreadId;
+#endif /* DEBUG */
                 if (!fAnonymous)
                     papszArgs[iOptIdx++] = szParmUserName;
@@ -1829,4 +1876,5 @@
                         RTPathStripExt(pszLogFile);
                         char *pszLogSuffix;
+#ifndef DEBUG
                         if (RTStrAPrintf(&pszLogSuffix, "-%RU32-%s",
                                          pSessionStartupInfo->uSessionID,
@@ -1835,4 +1883,13 @@
                             rc2 = VERR_NO_MEMORY;
                         }
+#else
+                        if (RTStrAPrintf(&pszLogSuffix, "-%RU32-%RU32-%s",
+                                         pSessionStartupInfo->uSessionID,
+                                         s_uCtrlSessionThread,
+                                         pSessionStartupInfo->szUser) < 0)
+                        {
+                            rc2 = VERR_NO_MEMORY;
+                        }
+#endif /* DEBUG */
                         else
                         {
@@ -1940,15 +1997,15 @@
                         hStdOutAndErr.enmType = RTHANDLETYPE_FILE;
 
-                        /** @todo Do we need a custom/cloned environment block? */
+                        /** @todo Set custom/cloned guest session environment block. */
                         rc = RTProcCreateEx(pszExeName, papszArgs, RTENV_DEFAULT, uProcFlags,
                                             &hStdIn, &hStdOutAndErr, &hStdOutAndErr,
-                                            !fAnonymous ? pSession->StartupInfo.szUser : NULL,
-                                            !fAnonymous ? pSession->StartupInfo.szPassword : NULL,
-                                            &pSession->hProcess);
+                                            !fAnonymous ? pSessionThread->StartupInfo.szUser : NULL,
+                                            !fAnonymous ? pSessionThread->StartupInfo.szPassword : NULL,
+                                            &pSessionThread->hProcess);
 
                         RTFileClose(hStdOutAndErr.u.hFile);
                     }
 
-                    RTFileClose(hStdOutAndErr.u.hFile);
+                    RTFileClose(hStdIn.u.hFile);
                 }
 #endif
@@ -1961,9 +2018,6 @@
         {
             /* Start session thread. */
-            static uint32_t s_uCtrlSessionThread = 0;
-            if (s_uCtrlSessionThread++ == UINT32_MAX)
-                s_uCtrlSessionThread = 0; /* Wrap around to not let IPRT freak out. */
-            rc = RTThreadCreateF(&pSession->Thread, gstcntlSessionThread,
-                                 pSession /*pvUser*/, 0 /*cbStack*/,
+            rc = RTThreadCreateF(&pSessionThread->Thread, gstcntlSessionThread,
+                                 pSessionThread /*pvUser*/, 0 /*cbStack*/,
                                  RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "sess%u", s_uCtrlSessionThread);
             if (RT_FAILURE(rc))
@@ -1974,11 +2028,10 @@
             {
                 /* Wait for the thread to initialize. */
-                rc = RTThreadUserWait(pSession->Thread, 60 * 1000 /* 60s timeout */);
-                AssertRC(rc);
-                if (   ASMAtomicReadBool(&pSession->fShutdown)
+                rc = RTThreadUserWait(pSessionThread->Thread, 60 * 1000 /* 60s timeout */);
+                if (   ASMAtomicReadBool(&pSessionThread->fShutdown)
                     || RT_FAILURE(rc))
                 {
                     VBoxServiceError("Thread for session ID=%RU32 failed to start, rc=%Rrc\n",
-                                     pSession->StartupInfo.uSessionID, rc);
+                                     pSessionThread->StartupInfo.uSessionID, rc);
                     if (RT_SUCCESS(rc))
                         rc = VERR_CANT_CREATE; /** @todo Find a better rc. */
@@ -1986,10 +2039,13 @@
                 else
                 {
-                    ASMAtomicXchgBool(&pSession->fStarted, true);
+                    VBoxServiceVerbose(2, "Thread for session ID=%RU32 started\n",
+                                       pSessionThread->StartupInfo.uSessionID);
+
+                    ASMAtomicXchgBool(&pSessionThread->fStarted, true);
 
                     /* Add session to list. */
-                    /* rc = */ RTListAppend(pList, &pSession->Node);
+                    /* rc = */ RTListAppend(pList, &pSessionThread->Node);
                     if (ppSessionThread) /* Return session if wanted. */
-                        *ppSessionThread = pSession;
+                        *ppSessionThread = pSessionThread;
                 }
             }
@@ -1998,5 +2054,5 @@
         if (RT_FAILURE(rc))
         {
-            RTMemFree(pSession);
+            RTMemFree(pSessionThread);
         }
     }
@@ -2004,5 +2060,5 @@
         rc = VERR_NO_MEMORY;
 
-    VBoxServiceVerbose(3, "Forking returned returned rc=%Rrc\n", rc);
+    VBoxServiceVerbose(3, "Forking session thread returned returned rc=%Rrc\n", rc);
     return rc;
 }
@@ -2010,12 +2066,13 @@
 
 /**
- * Closes a formerly opened guest session and removes it from
- * the session list.
+ * Waits for a formerly opened guest session process to close.
  *
  * @return  IPRT status code.
- * @param   pThread                 Guest session thread to close.
+ * @param   pThread                 Guest session thread to wait for.
+ * @param   uTimeoutMS              Waiting timeout (in ms).
  * @param   uFlags                  Closing flags.
  */
-int GstCntlSessionThreadClose(PVBOXSERVICECTRLSESSIONTHREAD pThread, uint32_t uFlags)
+int GstCntlSessionThreadWait(PVBOXSERVICECTRLSESSIONTHREAD pThread,
+                             uint32_t uTimeoutMS, uint32_t uFlags)
 {
     AssertPtrReturn(pThread, VERR_INVALID_POINTER);
@@ -2024,5 +2081,5 @@
     if (pThread->Thread == NIL_RTTHREAD)
     {
-        AssertMsgFailed(("Guest session thread of session %p does not exist when it should",
+        AssertMsgFailed(("Guest session thread of session %p does not exist when it should\n",
                          pThread));
         return VERR_NOT_FOUND;
@@ -2031,7 +2088,9 @@
     int rc = VINF_SUCCESS;
 
-    /* The fork should have received the same closing request,
+    /*
+     * The fork should have received the same closing request,
      * so just wait 30s for the process to close. On timeout kill
-     * it in a not so gentle manner. */
+     * it in a not so gentle manner.
+     */
     if (ASMAtomicReadBool(&pThread->fStarted))
     {
@@ -2039,11 +2098,9 @@
         ASMAtomicXchgBool(&pThread->fShutdown, true);
 
-        uint32_t cMsTimeout = 30 * 1000; /** @todo 30s default. Make this configurable. Later. */
-
         VBoxServiceVerbose(3, "Waiting for session thread ID=%RU32 to close (%RU32ms) ...\n",
-                           pThread->StartupInfo.uSessionID, cMsTimeout);
+                           pThread->StartupInfo.uSessionID, uTimeoutMS);
 
         int rcThread;
-        rc = RTThreadWait(pThread->Thread, cMsTimeout, &rcThread);
+        rc = RTThreadWait(pThread->Thread, uTimeoutMS, &rcThread);
         if (RT_FAILURE(rc))
         {
@@ -2054,19 +2111,31 @@
             VBoxServiceVerbose(3, "Session thread ID=%RU32 ended with rc=%Rrc\n",
                                pThread->StartupInfo.uSessionID, rcThread);
-
-        /* Remove session from list and destroy object. */
-        RTListNodeRemove(&pThread->Node);
-
-        if (RT_FAILURE(rc))
-            VBoxServiceError("Closing session ID=%RU32 failed with rc=%Rrc\n",
-                             pThread->StartupInfo.uSessionID, rc);
-
-        RTMemFree(pThread);
-        pThread = NULL;
-    }
-
-    return rc;
-}
-
+    }
+
+    return rc;
+}
+
+/**
+ * Waits for the specified session thread to end and remove
+ * it from the session thread list.
+ *
+ * @return  IPRT status code.
+ * @param   pThread                 Session thread to destroy.
+ * @param   uFlags                  Closing flags.
+ */
+int GstCntlSessionThreadDestroy(PVBOXSERVICECTRLSESSIONTHREAD pThread, uint32_t uFlags)
+{
+    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
+
+    int rc = GstCntlSessionThreadWait(pThread,
+                                      30 * 1000 /* 30s timeout */, uFlags);
+    /** @todo Kill session process if still around? */
+
+    /* Remove session from list and destroy object. */
+    RTListNodeRemove(&pThread->Node);
+    RTMemFree(pThread);
+
+    return rc;
+}
 
 /**
@@ -2077,5 +2146,5 @@
  * @param   uFlags                  Closing flags.
  */
-int GstCntlSessionThreadCloseAll(PRTLISTANCHOR pList, uint32_t uFlags)
+int GstCntlSessionThreadDestroyAll(PRTLISTANCHOR pList, uint32_t uFlags)
 {
     AssertPtrReturn(pList, VERR_INVALID_POINTER);
@@ -2083,16 +2152,18 @@
     int rc = VINF_SUCCESS;
 
-    PVBOXSERVICECTRLSESSIONTHREAD pSessionCur
+    PVBOXSERVICECTRLSESSIONTHREAD pSessionThread
          = RTListGetFirst(pList, VBOXSERVICECTRLSESSIONTHREAD, Node);
-    while (pSessionCur)
-    {
-        PVBOXSERVICECTRLSESSIONTHREAD pSessionNext =
-            RTListGetNext(pList, pSessionCur, VBOXSERVICECTRLSESSIONTHREAD, Node);
-        bool fLast = RTListNodeIsLast(pList, &pSessionCur->Node);
-
-        int rc2 = GstCntlSessionThreadClose(pSessionCur, uFlags);
-        if (RT_SUCCESS(rc))
-        {
-            rc = rc2;
+    while (pSessionThread)
+    {
+        PVBOXSERVICECTRLSESSIONTHREAD pSessionThreadNext =
+            RTListGetNext(pList, pSessionThread, VBOXSERVICECTRLSESSIONTHREAD, Node);
+        bool fLast = RTListNodeIsLast(pList, &pSessionThread->Node);
+
+        int rc2 = GstCntlSessionThreadDestroy(pSessionThread, uFlags);
+        if (RT_FAILURE(rc2))
+        {
+            VBoxServiceError("Closing session thread failed with rc=%Rrc\n", rc2);
+            if (RT_SUCCESS(rc))
+                rc = rc2;
             /* Keep going. */
         }
@@ -2101,10 +2172,9 @@
             break;
 
-        pSessionCur = pSessionNext;
-    }
-
-    return rc;
-}
-
+        pSessionThread = pSessionThreadNext;
+    }
+
+    return rc;
+}
 
 RTEXITCODE VBoxServiceControlSessionForkInit(int argc, char **argv)
@@ -2113,7 +2183,10 @@
     {
         { "--logfile",         VBOXSERVICESESSIONOPT_LOG_FILE,        RTGETOPT_REQ_STRING },
-        { "--username",        VBOXSERVICESESSIONOPT_USERNAME,        RTGETOPT_REQ_STRING },
+        { "--user",            VBOXSERVICESESSIONOPT_USERNAME,        RTGETOPT_REQ_STRING },
         { "--session-id",      VBOXSERVICESESSIONOPT_SESSION_ID,      RTGETOPT_REQ_UINT32 },
         { "--session-proto",   VBOXSERVICESESSIONOPT_SESSION_PROTO,   RTGETOPT_REQ_UINT32 },
+#ifdef DEBUG
+        { "--thread-id",       VBOXSERVICESESSIONOPT_THREAD_ID,       RTGETOPT_REQ_UINT32 },
+#endif /* DEBUG */
         { "--verbose",         'v',                                   RTGETOPT_REQ_NOTHING }
     };
@@ -2157,4 +2230,8 @@
                 break;
 
+            case VBOXSERVICESESSIONOPT_THREAD_ID:
+                /* Not handled. */
+                break;
+
             /** @todo Implement help? */
 
Index: /trunk/src/VBox/HostServices/GuestControl/service.cpp
===================================================================
--- /trunk/src/VBox/HostServices/GuestControl/service.cpp	(revision 45414)
+++ /trunk/src/VBox/HostServices/GuestControl/service.cpp	(revision 45415)
@@ -59,5 +59,8 @@
 *   Header Files                                                               *
 *******************************************************************************/
-#define LOG_GROUP LOG_GROUP_HGCM
+#ifdef LOG_GROUP
+ #undef LOG_GROUP
+#endif
+#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
 #include <VBox/HostServices/GuestControlSvc.h>
 
@@ -84,5 +87,5 @@
 
 /** Flag for indicating that the client only is interested in
- *  messages for specific contexts. */
+ *  messages of specific context IDs. */
 #define CLIENTSTATE_FLAG_CONTEXTFILTER      RT_BIT(0)
 
@@ -113,4 +116,8 @@
     uint32_t AddRef(void)
     {
+#ifdef DEBUG_andy
+        LogFlowFunc(("Adding reference pHostCmd=%p, CID=%RU32, new refCount=%RU32\n",
+                     this, mContextID, mRefCount + 1));
+#endif
         return ++mRefCount;
     }
@@ -118,7 +125,8 @@
     uint32_t Release(void)
     {
-        LogFlowFunc(("Releasing CID=%RU32, refCount=%RU32\n",
-                     mContextID, mRefCount));
-
+#ifdef DEBUG_andy
+        LogFlowFunc(("Releasing reference pHostCmd=%p, CID=%RU32, new refCount=%RU32\n",
+                     this, mContextID, mRefCount - 1));
+#endif
         /* Release reference for current command. */
         Assert(mRefCount);
@@ -139,6 +147,6 @@
     int Allocate(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
     {
-        LogFlowFunc(("Allocating uMsg=%RU32, cParms=%RU32, paParms=%p\n",
-                     uMsg, cParms, paParms));
+        LogFlowFunc(("Allocating pHostCmd=%p, uMsg=%RU32, cParms=%RU32, paParms=%p\n",
+                     this, uMsg, cParms, paParms));
 
         if (!cParms) /* At least one parameter (context ID) must be present. */
@@ -219,4 +227,8 @@
              */
             rc = mpParms[0].getUInt32(&mContextID);
+
+            /* Set timestamp so that clients can distinguish between already
+             * processed commands and new ones. */
+            mTimestamp = RTTimeNanoTS();
         }
 
@@ -232,9 +244,9 @@
     void Free(void)
     {
-        AssertMsg(mRefCount == 0, ("Command CID=%RU32 still being used by a client (%RU32 refs), cannot free yet\n",
-                                   mContextID, mRefCount));
-
-        LogFlowFunc(("Freeing host command CID=%RU32, mMsgType=%RU32, mParmCount=%RU32, mpParms=%p\n",
-                     mContextID, mMsgType, mParmCount, mpParms));
+        AssertMsg(mRefCount == 0, ("pHostCmd=%p, CID=%RU32 still being used by a client (%RU32 refs), cannot free yet\n",
+                                   this, mContextID, mRefCount));
+
+        LogFlowFunc(("Freeing host command pHostCmd=%p, CID=%RU32, mMsgType=%RU32, mParmCount=%RU32, mpParms=%p\n",
+                     this, mContextID, mMsgType, mParmCount, mpParms));
 
         for (uint32_t i = 0; i < mParmCount; i++)
@@ -260,6 +272,6 @@
         mParmCount = 0;
 
-       /* Removes the command from its list */
-       RTListNodeRemove(&Node);
+        /* Removes the command from its list */
+        RTListNodeRemove(&Node);
     }
 
@@ -274,4 +286,7 @@
     int CopyTo(VBOXHGCMSVCPARM paDstParms[], uint32_t cDstParms) const
     {
+        LogFlowFunc(("pHostCmd=%p, mMsgType=%RU32, mParmCount=%RU32, mContextID=%RU32\n",
+                     this, mMsgType, mParmCount, mContextID));
+
         int rc = VINF_SUCCESS;
         if (cDstParms != mParmCount)
@@ -294,8 +309,4 @@
                 else
                 {
-#ifdef DEBUG_andy
-                    LogFlowFunc(("\tmpParms[%RU32] type = %RU32\n",
-                                 i, mpParms[i].type));
-#endif
                     switch (mpParms[i].type)
                     {
@@ -362,6 +373,6 @@
         int rc;
 
-        LogFlowFunc(("mMsgType=%RU32, mParmCount=%RU32, mpParms=%p\n",
-                     mMsgType, mParmCount, mpParms));
+        LogFlowFunc(("pHostCmd=%p, mMsgType=%RU32, mParmCount=%RU32, mpParms=%p\n",
+                     this, mMsgType, mParmCount, mpParms));
 
         /* Does the current host command need more parameter space which
@@ -392,5 +403,4 @@
         }
 
-        LogFlowFunc(("Returned with rc=%Rrc\n", rc));
         return rc;
     }
@@ -400,8 +410,6 @@
         AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
 
-        LogFlowFunc(("mMsgType=%RU32, mParmCount=%RU32, mpParms=%p\n",
-                     mMsgType, mParmCount, mpParms));
-        LogFlowFunc(("Telling client the next upcoming message type=%RU32, count=%RU32\n",
-                     mMsgType, mParmCount));
+        LogFlowFunc(("pHostCmd=%p, mMsgType=%RU32, mParmCount=%RU32, mpParms=%p\n",
+                     this, mMsgType, mParmCount, mpParms));
 
         if (pConnection->mNumParms >= 2)
@@ -431,7 +439,14 @@
     /** Dynamic structure for holding the HGCM parms */
     uint32_t mMsgType;
+    /** Number of HGCM parameters. */
     uint32_t mParmCount;
+    /** Array of HGCM parameters. */
     PVBOXHGCMSVCPARM mpParms;
+    /** Incoming timestamp (us). */
+    uint64_t mTimestamp;
 } HostCommand;
+typedef std::list< HostCommand *> HostCmdList;
+typedef std::list< HostCommand *>::iterator HostCmdListIter;
+typedef std::list< HostCommand *>::const_iterator HostCmdListIterConst;
 
 /**
@@ -459,14 +474,89 @@
 {
     ClientState(void)
-        : mSvcHelpers(NULL),
+        : mID(0),
+          mSvcHelpers(NULL),
           mFlags(0), mContextFilter(0),
-          mpHostCmd(NULL), mHostCmdRc(VINF_SUCCESS), mHostCmdTries(0),
-          mIsPending(false) {}
-
-    ClientState(PVBOXHGCMSVCHELPERS pSvcHelpers)
-        : mSvcHelpers(pSvcHelpers),
+          mHostCmdRc(VINF_SUCCESS), mHostCmdTries(0),
+          mHostCmdTS(0),
+          mIsPending(false) { }
+
+    ClientState(PVBOXHGCMSVCHELPERS pSvcHelpers, uint32_t uClientID)
+        : mID(uClientID),
+          mSvcHelpers(pSvcHelpers),
           mFlags(0), mContextFilter(0),
-          mpHostCmd(NULL), mHostCmdRc(VINF_SUCCESS), mHostCmdTries(0),
-          mIsPending(false) {}
+          mHostCmdRc(VINF_SUCCESS), mHostCmdTries(0),
+          mHostCmdTS(0),
+          mIsPending(false) { }
+
+    void DequeueAll(void)
+    {
+        HostCmdListIter curItem = mHostCmdList.begin();
+        while (curItem != mHostCmdList.end())
+            Dequeue(curItem++);
+    }
+
+    void DequeueCurrent(void)
+    {
+        HostCmdListIter curCmd = mHostCmdList.begin();
+        if (curCmd != mHostCmdList.end())
+            Dequeue(curCmd);
+    }
+
+    void Dequeue(HostCommand *pHostCmd)
+    {
+        AssertPtrReturnVoid(pHostCmd);
+
+        HostCmdListIter curItem = mHostCmdList.begin();
+        while (curItem != mHostCmdList.end())
+        {
+            if ((*curItem) == pHostCmd)
+            {
+                Dequeue(curItem);
+                break;
+            }
+
+            curItem++;
+        }
+    }
+
+    void Dequeue(HostCmdListIter &curItem)
+    {
+        HostCommand *pHostCmd = (*curItem);
+        AssertPtr(pHostCmd);
+
+        if (pHostCmd->Release() == 0)
+        {
+            LogFlowFunc(("[Client %RU32] Destroying pHostCmd=%p\n",
+                         mID, (*curItem)));
+
+            delete pHostCmd;
+            pHostCmd = NULL;
+        }
+
+        mHostCmdList.erase(curItem);
+
+        /* Reset everything else. */
+        mHostCmdRc    = VINF_SUCCESS;
+        mHostCmdTries = 0;
+    }
+
+    int EnqueueCommand(HostCommand *pHostCmd)
+    {
+        AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER);
+
+        int rc = VINF_SUCCESS;
+
+        try
+        {
+            mHostCmdList.push_back(pHostCmd);
+            pHostCmd->AddRef();
+        }
+        catch (std::bad_alloc)
+        {
+            rc = VERR_NO_MEMORY;
+        }
+
+        return rc;
+    }
 
     bool WantsHostCommand(const HostCommand *pHostCmd) const
@@ -475,19 +565,30 @@
 
 #ifdef DEBUG_andy
-            LogFlowFunc(("mFlags=%x, mContextID=%RU32, mContextFilter=%x, filterRes=%x\n",
-                         mFlags, pHostCmd->mContextID, mContextFilter, pHostCmd->mContextID & mContextFilter));
+        LogFlowFunc(("mHostCmdTS=%RU64, pHostCmdTS=%RU64\n",
+                     mHostCmdTS, pHostCmd->mTimestamp));
+#endif
+
+        /* Only process newer commands. */
+        if (pHostCmd->mTimestamp <= mHostCmdTS)
+            return false;
+
+#ifdef DEBUG_andy
+            LogFlowFunc(("[Client %RU32] mFlags=%x, mContextID=%RU32, mContextFilter=%x, filterRes=%x, sessionID=%RU32\n",
+                         mID, mFlags, pHostCmd->mContextID, mContextFilter,
+                         pHostCmd->mContextID & mContextFilter, VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pHostCmd->mContextID)));
 #endif
         /*
          * If a sesseion filter is set, only obey those sessions we're interested in.
          */
+        bool fWant = false;
         if (mFlags & CLIENTSTATE_FLAG_CONTEXTFILTER)
         {
             if ((pHostCmd->mContextID & mContextFilter) == mContextFilter)
-                return true;
+                fWant = true;
         }
         else /* Client is interested in all commands. */
-            return true;
-
-        return false;
+            fWant = true;
+
+        return fWant;
     }
 
@@ -496,12 +597,7 @@
         AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
 
-        LogFlowFunc(("mIsPending=%RTbool, mpHostCmd=%p, CID=%RU32, type=%RU32\n",
-                     mIsPending, mpHostCmd,
-                     mpHostCmd ? mpHostCmd->mContextID : 0,
-                     mpHostCmd ? mpHostCmd->mMsgType : 0));
-
         if (mIsPending)
         {
-            LogFlowFunc(("Client already is in pending mode\n"));
+            LogFlowFunc(("[Client %RU32] Already is in pending mode\n", mID));
 
             /*
@@ -511,16 +607,16 @@
         }
 
-        if (mpHostCmd == NULL)
+        if (mHostCmdList.empty())
         {
             AssertMsg(mIsPending == false,
-                      ("Client %p already is pending but tried to receive a new host command\n", this));
-
-            mPending.mHandle   = pConnection->mHandle;
-            mPending.mNumParms = pConnection->mNumParms;
-            mPending.mParms    = pConnection->mParms;
+                      ("Client ID=%RU32 already is pending but tried to receive a new host command\n", mID));
+
+            mPendingCon.mHandle   = pConnection->mHandle;
+            mPendingCon.mNumParms = pConnection->mNumParms;
+            mPendingCon.mParms    = pConnection->mParms;
 
             mIsPending = true;
 
-            LogFlowFunc(("Client now is in pending mode\n"));
+            LogFlowFunc(("[Client %RU32] Is now in pending mode\n", mID));
 
             /*
@@ -538,177 +634,123 @@
     }
 
-    int SetNextCommand(HostCommand *pHostCmd)
-    {
+    int Run(const ClientConnection *pConnection,
+                  HostCommand      *pHostCmd)
+    {
+        AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
         AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER);
 
-        mpHostCmd = pHostCmd;
-        AssertPtr(mpHostCmd);
-        mpHostCmd->AddRef();
-
-        /* Create a command context to keep track of client-specific
-         * information about a certain command. */
-        Assert(mContextMap.find(mpHostCmd->mContextID) == mContextMap.end());
-        mContextMap[mpHostCmd->mContextID] = ClientContext(mpHostCmd);
-        /** @todo Exception handling! */
-
-        LogFlowFunc(("Assigning next host comamnd CID=%RU32, cmdType=%RU32, cmdParms=%RU32, new refCount=%RU32\n",
-                     mpHostCmd->mContextID, mpHostCmd->mMsgType, mpHostCmd->mParmCount, mpHostCmd->mRefCount));
-
-        return VINF_SUCCESS;
-    }
-
-    int Run(const ClientConnection *pConnection,
-            const RTLISTANCHOR     *pHostCmdList)
-    {
         int rc = VINF_SUCCESS;
 
-        LogFlowFunc(("Client pConnection=%p, pHostCmdList=%p\n",
-                      pConnection, pHostCmdList));
-        LogFlowFunc(("Client hostCmd=%p, mHostCmdRc=%Rrc, mHostCmdTries=%RU32\n",
-                      mpHostCmd, mHostCmdRc, mHostCmdTries));
-
-        /* No current command? Try getting a new one to process now. */
-        if (mpHostCmd == NULL)
-        {
-            /* Get the next host command the clienet is interested in. */
-            bool fFoundCmd = false;
-            HostCommand *pCurCmd;
-            RTListForEach(pHostCmdList, pCurCmd, HostCommand, Node)
+        LogFlowFunc(("[Client %RU32] pConnection=%p, mHostCmdRc=%Rrc, mHostCmdTries=%RU32\n",
+                      mID, pConnection, mHostCmdRc, mHostCmdTries));
+
+        mHostCmdRc = SendReply(pConnection, pHostCmd);
+        LogFlowFunc(("[Client %RU32] Processing pHostCmd=%p ended with rc=%Rrc\n",
+                     mID, pHostCmd, mHostCmdRc));
+
+        bool fRemove = false;
+        if (RT_FAILURE(mHostCmdRc))
+        {
+            mHostCmdTries++;
+
+            /*
+             * If the client understood the message but supplied too little buffer space
+             * don't send this message again and drop it after 3 unsuccessful attempts.
+             * The host then should take care of next actions (maybe retry it with a smaller buffer).
+             */
+            if (mHostCmdRc == VERR_TOO_MUCH_DATA)
             {
-                fFoundCmd = WantsHostCommand(pCurCmd);
-                if (fFoundCmd)
-                {
-                    int rc2 = SetNextCommand(pCurCmd);
-                    if (RT_SUCCESS(rc2))
-                        break;
-                }
-            }
-
-            LogFlowFunc(("Client %s new command\n",
-                         fFoundCmd ? "found" : "did not find a"));
-
-            /* If no new command was found, set client into pending state. */
-            if (!fFoundCmd)
-                rc = SetPending(pConnection);
-        }
-
-        if (mpHostCmd)
-        {
-            AssertPtr(mpHostCmd);
-            mHostCmdRc = SendReply(pConnection, mpHostCmd);
-            LogFlowFunc(("Processing command CID=%RU32 ended with rc=%Rrc\n",
-                         mpHostCmd->mContextID, mHostCmdRc));
-
-            bool fRemove = false;
-            if (RT_FAILURE(mHostCmdRc))
-            {
-                mHostCmdTries++;
-
-                /*
-                 * If the client understood the message but supplied too little buffer space
-                 * don't send this message again and drop it after 3 unsuccessful attempts.
-                 * The host then should take care of next actions (maybe retry it with a smaller buffer).
-                 */
-                if (   mHostCmdRc    == VERR_TOO_MUCH_DATA
-                    && mHostCmdTries >= 3)
-                {
-                    fRemove = true;
-                }
-                /* Client did not understand the message or something else weird happened. Try again one
-                 * more time and drop it if it didn't get handled then. */
-                else if (mHostCmdTries > 1)
+                if (mHostCmdTries >= 3)
                     fRemove = true;
             }
+            /* Client did not understand the message or something else weird happened. Try again one
+             * more time and drop it if it didn't get handled then. */
+            else if (mHostCmdTries > 1)
+                fRemove = true;
+        }
+        else
+            fRemove = true; /* Everything went fine, remove it. */
+
+        LogFlowFunc(("[Client %RU32] Tried pHostCmd=%p for %RU32 times, (last result=%Rrc, fRemove=%RTbool)\n",
+                     mID, pHostCmd, mHostCmdTries, mHostCmdRc, fRemove));
+
+        if (RT_SUCCESS(rc))
+            rc = mHostCmdRc;
+
+        if (fRemove)
+            Dequeue(pHostCmd);
+
+        LogFlowFunc(("[Client %RU32] Returned with rc=%Rrc\n", mID, rc));
+        return rc;
+    }
+
+    int RunCurrent(const ClientConnection *pConnection)
+    {
+        AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
+
+        int rc;
+        if (mHostCmdList.empty())
+        {
+            rc = SetPending(pConnection);
+        }
+        else
+        {
+            AssertMsgReturn(!mIsPending,
+                            ("Client ID=%RU32 still is in pending mode; can't use another connection\n", mID), VERR_INVALID_PARAMETER);
+
+            HostCmdListIter curCmd = mHostCmdList.begin();
+            Assert(curCmd != mHostCmdList.end());
+            HostCommand *pHostCmd = (*curCmd);
+            AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER);
+
+            rc = Run(pConnection, pHostCmd);
+        }
+
+        return rc;
+    }
+
+    int Wakeup(void)
+    {
+        int rc = VINF_NO_CHANGE;
+
+        if (mIsPending)
+        {
+            LogFlowFunc(("[Client %RU32] Waking up ...\n", mID));
+
+            rc = VINF_SUCCESS;
+
+            HostCmdListIter curCmd = mHostCmdList.begin();
+            if (curCmd != mHostCmdList.end())
+            {
+                HostCommand *pHostCmd = (*curCmd);
+                AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER);
+
+                LogFlowFunc(("[Client %RU32] Current host command is pHostCmd=%p, CID=%RU32, cmdType=%RU32, cmdParms=%RU32, refCount=%RU32\n",
+                             mID, pHostCmd, pHostCmd->mContextID, pHostCmd->mMsgType, pHostCmd->mParmCount, pHostCmd->mRefCount));
+
+                rc = Run(&mPendingCon, pHostCmd);
+            }
             else
-                fRemove = true; /* Everything went fine, remove it. */
-
-            LogFlowFunc(("Client tried CID=%RU32 for %RU32 times, (last result=%Rrc, fRemove=%RTbool)\n",
-                         mpHostCmd->mContextID, mHostCmdTries, mHostCmdRc, fRemove));
-
-            if (fRemove)
-            {
-                /* Try fetching next command. */
-                HostCommand *pCmdNext = RTListGetNext(pHostCmdList, mpHostCmd, HostCommand, Node);
-
-                LogFlowFunc(("Client removes itself from command CID=%RU32 (next command: %p, CID=%RU32)\n",
-                              mpHostCmd->mContextID, pCmdNext, pCmdNext ? pCmdNext->mContextID : 0));
-
-                /* Remove command from context map. */
-                /** @todo Exception handling! */
-                mContextMap.erase(mpHostCmd->mContextID);
-
-                /* Release reference for current command. */
-                if (mpHostCmd->Release() == 0)
-                {
-                    LogFlowFunc(("Destroying command CID=%RU32\n",
-                                 mpHostCmd->mContextID));
-
-                    RTMemFree(mpHostCmd);
-                }
-
-                /* Assign next command (if any) to this client. */
-                if (pCmdNext)
-                {
-                    rc = SetNextCommand(pCmdNext);
-                }
-                else
-                    mpHostCmd = NULL;
-
-                /* Reset everything else. */
-                mHostCmdRc    = VINF_SUCCESS;
-                mHostCmdTries = 0;
-            }
-
-            if (RT_SUCCESS(rc))
-                rc = mHostCmdRc;
-        }
-
-        LogFlowFunc(("Returned with rc=%Rrc\n", rc));
-        return rc;
-    }
-
-    int RunNow(const ClientConnection *pConnection,
-               const PRTLISTANCHOR     pHostCmdList)
-    {
-        AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
-        AssertPtrReturn(pHostCmdList, VERR_INVALID_POINTER);
-
-        AssertMsgReturn(!mIsPending, ("Can't use another connection when client still is in pending mode\n"),
-                        VERR_INVALID_PARAMETER);
-
-        int rc = Run(pConnection, pHostCmdList);
-
-        LogFlowFunc(("Returned with rc=%Rrc\n"));
-        return rc;
-    }
-
-    int Wakeup(const PRTLISTANCHOR pHostCmdList)
-    {
-        AssertPtrReturn(pHostCmdList, VERR_INVALID_POINTER);
-        AssertMsgReturn(mIsPending, ("Cannot wake up a client which is not in pending mode\n"),
-                        VERR_INVALID_PARAMETER);
-
-        int rc = Run(&mPending, pHostCmdList);
-
-        /* Reset pending state. */
-        mIsPending = false;
-
-        LogFlowFunc(("Returned with rc=%Rrc\n"));
-        return rc;
+                AssertMsgFailed(("Waking up client ID=%RU32 with no host command in queue is a bad idea\n", mID));
+
+            return rc;
+        }
+
+        return VINF_NO_CHANGE;
     }
 
     int CancelWaiting(int rcPending)
     {
-        LogFlowFunc(("Cancelling waiting with %Rrc, isPending=%RTbool, pendingNumParms=%RU32, flags=%x\n",
-                     rcPending, mIsPending, mPending.mNumParms, mFlags));
+        LogFlowFunc(("[Client %RU32] Cancelling waiting with %Rrc, isPending=%RTbool, pendingNumParms=%RU32, flags=%x\n",
+                     mID, rcPending, mIsPending, mPendingCon.mNumParms, mFlags));
 
         if (   mIsPending
-            && mPending.mNumParms >= 2)
-        {
-            mPending.mParms[0].setUInt32(HOST_CANCEL_PENDING_WAITS); /* Message ID. */
-            mPending.mParms[1].setUInt32(0);                         /* Required parameters for message. */
+            && mPendingCon.mNumParms >= 2)
+        {
+            mPendingCon.mParms[0].setUInt32(HOST_CANCEL_PENDING_WAITS); /* Message ID. */
+            mPendingCon.mParms[1].setUInt32(0);                         /* Required parameters for message. */
 
             AssertPtr(mSvcHelpers);
-            mSvcHelpers->pfnCallComplete(mPending.mHandle, rcPending);
+            mSvcHelpers->pfnCallComplete(mPendingCon.mHandle, rcPending);
 
             mIsPending = false;
@@ -740,4 +782,7 @@
         }
 
+        /* Reset pending status. */
+        mIsPending = false;
+
         /* In any case the client did something, so complete
          * the pending call with the result we just got. */
@@ -745,16 +790,18 @@
         mSvcHelpers->pfnCallComplete(pConnection->mHandle, rc);
 
-        LogFlowFunc(("pConnection=%p, pHostCmd=%p, rc=%Rrc\n",
-                     pConnection, pHostCmd, rc));
+        LogFlowFunc(("[Client %RU32] pConnection=%p, pHostCmd=%p, replyRc=%Rrc\n",
+                     mID, pConnection, pHostCmd, rc));
         return rc;
     }
 
     PVBOXHGCMSVCHELPERS mSvcHelpers;
+    /** The client's ID. */
+    uint32_t mID;
     /** Client flags. @sa CLIENTSTATE_FLAG_ flags. */
     uint32_t mFlags;
     /** The context ID filter, based on the flags set. */
     uint32_t mContextFilter;
-    /** Pointer to current host command to process. */
-    HostCommand *mpHostCmd;
+    /** Host command list to process. */
+    HostCmdList mHostCmdList;
     /** Last (most recent) rc after handling the
      *  host command. */
@@ -763,10 +810,10 @@
      *  command to the according client. */
     uint32_t mHostCmdTries;
-    /** Map containing all context IDs a client is assigned to. */
-    ClientContextMap mContextMap;
+    /** Timestamp (us) of last host command processed. */
+    uint64_t mHostCmdTS;
     /** Flag indicating whether the client currently is pending. */
     bool mIsPending;
     /** The client's pending connection. */
-    ClientConnection mPending;
+    ClientConnection mPendingCon;
 } ClientState;
 typedef std::map< uint32_t, ClientState > ClientStateMap;
@@ -831,6 +878,6 @@
     {
         AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
-        LogFlowFunc (("pvService=%p, u32ClientID=%u, pvClient=%p\n", pvService, u32ClientID, pvClient));
         SELF *pSelf = reinterpret_cast<SELF *>(pvService);
+        AssertPtrReturn(pSelf, VERR_INVALID_POINTER);
         return pSelf->clientConnect(u32ClientID, pvClient);
     }
@@ -845,6 +892,6 @@
     {
         AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
-        LogFlowFunc (("pvService=%p, u32ClientID=%u, pvClient=%p\n", pvService, u32ClientID, pvClient));
         SELF *pSelf = reinterpret_cast<SELF *>(pvService);
+        AssertPtrReturn(pSelf, VERR_INVALID_POINTER);
         return pSelf->clientDisconnect(u32ClientID, pvClient);
     }
@@ -863,7 +910,6 @@
     {
         AssertLogRelReturnVoid(VALID_PTR(pvService));
-        LogFlowFunc (("pvService=%p, callHandle=%p, u32ClientID=%u, pvClient=%p, u32Function=%u, cParms=%u, paParms=%p\n",
-                      pvService, callHandle, u32ClientID, pvClient, u32Function, cParms, paParms));
         SELF *pSelf = reinterpret_cast<SELF *>(pvService);
+        AssertPtrReturnVoid(pSelf);
         pSelf->call(callHandle, u32ClientID, pvClient, u32Function, cParms, paParms);
     }
@@ -879,6 +925,6 @@
     {
         AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
-        LogFlowFunc (("pvService=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, u32Function, cParms, paParms));
         SELF *pSelf = reinterpret_cast<SELF *>(pvService);
+        AssertPtrReturn(pSelf, VERR_INVALID_POINTER);
         return pSelf->hostCall(u32Function, cParms, paParms);
     }
@@ -894,4 +940,5 @@
         AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
         SELF *pSelf = reinterpret_cast<SELF *>(pvService);
+        AssertPtrReturn(pSelf, VERR_INVALID_POINTER);
         pSelf->mpfnHostCallback = pfnExtension;
         pSelf->mpvHostData = pvExtension;
@@ -906,4 +953,5 @@
     int clientGetCommand(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
     int clientSetMsgFilter(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+    int clientSkipMsg(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
     int cancelHostCmd(uint32_t u32ContextID);
     int cancelPendingWaits(uint32_t u32ClientID, int rcPending);
@@ -924,5 +972,5 @@
 int Service::clientConnect(uint32_t u32ClientID, void *pvClient)
 {
-    LogFlowFunc(("New client with ID=%RU32 connected\n", u32ClientID));
+    LogFlowFunc(("[Client %RU32] Connected\n", u32ClientID));
 #ifdef VBOX_STRICT
     ClientStateMapIterConst it = mClientStateMap.find(u32ClientID);
@@ -934,6 +982,6 @@
     }
 #endif
-    ClientState cs(mpHelpers);
-    mClientStateMap[u32ClientID] = cs;
+    ClientState clientState(mpHelpers, u32ClientID);
+    mClientStateMap[u32ClientID] = clientState;
     /** @todo Exception handling! */
     return VINF_SUCCESS;
@@ -951,68 +999,28 @@
 int Service::clientDisconnect(uint32_t u32ClientID, void *pvClient)
 {
-    LogFlowFunc(("Client with ID=%RU32 (%zu clients total) disconnected\n",
+    LogFlowFunc(("[Client %RU32] Disonnected (%zu clients total)\n",
                  u32ClientID, mClientStateMap.size()));
 
-    /* If this was the last connected (guest) client we need to
-     * unblock all eventually queued up (waiting) host calls. */
+    AssertMsg(mClientStateMap.size(),
+              ("No clients in list anymore when there should (client ID=%RU32)\n", u32ClientID));
+
+    int rc = VINF_SUCCESS;
+
+    ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID);
+    AssertMsg(itClientState != mClientStateMap.end(),
+              ("Clients ID=%RU32 not found in client list when it should be there\n", u32ClientID));
+
+    if (itClientState != mClientStateMap.end())
+    {
+        itClientState->second.DequeueAll();
+
+        mClientStateMap.erase(itClientState);
+    }
+
     bool fAllClientsDisconnected = mClientStateMap.size() == 0;
     if (fAllClientsDisconnected)
-        LogFlowFunc(("No connected clients left, notifying all queued up host callbacks\n"));
-
-    /*
-     * Throw out all stale clients.
-     */
-    int rc = VINF_SUCCESS;
-
-    ClientStateMapIter itClientState = mClientStateMap.begin();
-    while (   itClientState != mClientStateMap.end()
-           && RT_SUCCESS(rc))
-    {
-        /*
-         * Unblock/call back all queued items of the specified client
-         * or for all items in case there is no waiting client around
-         * anymore.
-         */
-        if (   itClientState->first == u32ClientID
-            || fAllClientsDisconnected)
-        {
-            LogFlowFunc(("Cancelling %RU32 context(s) of client ID=%RU32\n",
-                         itClientState->second.mContextMap.size(), u32ClientID));
-
-            ClientContextMapIter itContext = itClientState->second.mContextMap.begin();
-            while (itContext != itClientState->second.mContextMap.end())
-            {
-                uint32_t uContextID = itContext->first;
-
-                /*
-                 * Notify the host that clients with u32ClientID are no longer
-                 * around and need to be cleaned up (canceling waits etc).
-                 */
-                LogFlowFunc(("Notifying CID=%RU32 of disconnect ...\n", uContextID));
-                int rc2 = cancelHostCmd(uContextID);
-                if (RT_FAILURE(rc2))
-                {
-                    LogFlowFunc(("Cancelling host command with CID=%RU32 failed with rc=%Rrc\n",
-                                 uContextID, rc2));
-                    /* Keep going. */
-                }
-
-                AssertPtr(itContext->second.mpHostCmd);
-                itContext->second.mpHostCmd->Release();
-
-                itContext++;
-            }
-
-            itClientState->second.mContextMap.clear();
-
-            /** @todo Exception handling! */
-            mClientStateMap.erase(itClientState++);
-        }
-        else
-            itClientState++;
-    }
-
-    if (fAllClientsDisconnected)
-    {
+    {
+        LogFlowFunc(("All clients disconnected, cancelling all host commands ...\n"));
+
         /*
          * If all clients disconnected we also need to make sure that all buffered
@@ -1034,8 +1042,8 @@
             }
 
-            pCurCmd->Free();
-
-            RTListNodeRemove(&pCurCmd->Node);
-            RTMemFree(pCurCmd);
+            while (pCurCmd->Release());
+
+            delete pCurCmd;
+            pCurCmd = NULL;
 
             if (fLast)
@@ -1082,21 +1090,5 @@
     thisCon.mParms    = paParms;
 
-    /*
-     * If host command list is empty (nothing to do right now) just
-     * defer the call until we got something to do (makes the client
-     * wait).
-     */
-    int rc;
-    if (RTListIsEmpty(&mHostCmdList))
-    {
-        rc = clientState.SetPending(&thisCon);
-    }
-    else
-    {
-        rc = clientState.RunNow(&thisCon, &mHostCmdList);
-    }
-
-    LogFlowFunc(("Returned with rc=%Rrc\n", rc));
-    return rc;
+    return clientState.RunCurrent(&thisCon);
 }
 
@@ -1123,6 +1115,4 @@
     if (RT_SUCCESS(rc))
     {
-        /* paParms[1] unused yet. */
-
         ClientState &clientState = itClientState->second;
 
@@ -1138,6 +1128,28 @@
     }
 
-    LogFlowFunc(("Returned with rc=%Rrc\n", rc));
     return rc;
+}
+
+int Service::clientSkipMsg(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle,
+                           uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+    /*
+     * Lookup client in our list so that we can assign the context ID of
+     * a command to that client.
+     */
+    ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID);
+    AssertMsg(itClientState != mClientStateMap.end(), ("Client ID=%RU32 not found when it should be present\n",
+                                                       u32ClientID));
+    if (itClientState == mClientStateMap.end())
+        return VERR_NOT_FOUND; /* Should never happen. */
+
+    if (cParms != 0)
+        return VERR_INVALID_PARAMETER;
+
+    LogFlowFunc(("Client ID=%RU32 skipping message ...\n", u32ClientID));
+
+    itClientState->second.DequeueCurrent();
+
+    return VINF_SUCCESS;
 }
 
@@ -1228,18 +1240,22 @@
 
     int rc;
-    HostCommand *pHostCmd = (HostCommand*)RTMemAllocZ(sizeof(HostCommand));
-    if (pHostCmd)
-    {
+
+    HostCommand *pHostCmd = NULL;
+    try
+    {
+        pHostCmd = new HostCommand();
         rc = pHostCmd->Allocate(eFunction, cParms, paParms);
         if (RT_SUCCESS(rc))
-            RTListAppend(&mHostCmdList, &pHostCmd->Node);
-    }
-    else
+            /* rc = */ RTListAppend(&mHostCmdList, &pHostCmd->Node);
+    }
+    catch (std::bad_alloc)
+    {
         rc = VERR_NO_MEMORY;
+    }
 
     if (RT_SUCCESS(rc))
     {
-        LogFlowFunc(("Handling host command CID=%RU32, numClients=%zu\n",
-                     pHostCmd->mContextID, mClientStateMap.size()));
+        LogFlowFunc(("Handling host command CID=%RU32, eFunction=%RU32, cParms=%RU32, paParms=%p, numClients=%zu\n",
+                     pHostCmd->mContextID, eFunction, cParms, paParms, mClientStateMap.size()));
 
         /*
@@ -1250,36 +1266,33 @@
         uint32_t uClientsWokenUp = 0;
 #endif
-
         ClientStateMapIter itClientState = mClientStateMap.begin();
         AssertMsg(itClientState != mClientStateMap.end(), ("Client state map is empty when it should not\n"));
         while (itClientState != mClientStateMap.end())
         {
-            if (itClientState->second.mIsPending) /* Only wake up pending clients. */
+            ClientState &clientState = itClientState->second;
+
+            /* If a client indicates that it it wants the new host command,
+             * add a reference to not delete it.*/
+            if (clientState.WantsHostCommand(pHostCmd))
             {
-                LogFlowFunc(("Waking up client ID=%RU32 (isPending=%RTbool) ...\n",
-                             itClientState->first, itClientState->second.mIsPending));
-
-                ClientState &clientState = itClientState->second;
-                int rc2 = clientState.Wakeup(&mHostCmdList);
-                LogFlowFunc(("Client ID=%RU32 wakeup ended with rc=%Rrc\n",
-                             itClientState->first, rc2));
-#ifdef DEBUG
+                clientState.EnqueueCommand(pHostCmd);
+
+                int rc2 = clientState.Wakeup();
+                if (RT_FAILURE(rc2))
+                    LogFlowFunc(("Waking up client ID=%RU32 failed with rc=%Rrc\n",
+                                 itClientState->first, rc2));
+#ifdef DEBUG_andy
                 uClientsWokenUp++;
 #endif
             }
-            else
-                LogFlowFunc(("Client ID=%RU32 is not in pending state\n",
-                             itClientState->first));
 
             itClientState++;
         }
 
-#ifdef DEBUG
-        LogFlowFunc(("%RU32 clients have been succcessfully woken up\n",
-                      uClientsWokenUp));
+#ifdef DEBUG_andy
+        LogFlowFunc(("%RU32 clients have been woken up\n", uClientsWokenUp));
 #endif
     }
 
-    LogFlowFunc(("Returned with rc=%Rrc\n", rc));
     return rc;
 }
@@ -1299,5 +1312,5 @@
 {
     int rc = VINF_SUCCESS;
-    LogFlowFunc(("u32ClientID=%RU32, fn=%RU32, cParms=%RU32, paParms=0x%p\n",
+    LogFlowFunc(("[Client %RU32] eFunction=%RU32, cParms=%RU32, paParms=0x%p\n",
                  u32ClientID, eFunction, cParms, paParms));
     try
@@ -1308,5 +1321,5 @@
         if (eFunction == GUEST_MSG_WAIT)
         {
-            LogFlowFunc(("GUEST_MSG_GET\n"));
+            LogFlowFunc(("[Client %RU32] GUEST_MSG_GET\n", u32ClientID));
             rc = clientGetCommand(u32ClientID, callHandle, cParms, paParms);
         }
@@ -1321,5 +1334,5 @@
                  */
                 case GUEST_CANCEL_PENDING_WAITS:
-                    LogFlowFunc(("GUEST_CANCEL_PENDING_WAITS\n"));
+                    LogFlowFunc(("[Client %RU32] GUEST_CANCEL_PENDING_WAITS\n", u32ClientID));
                     rc = cancelPendingWaits(u32ClientID, VINF_SUCCESS /* Pending result */);
                     break;
@@ -1330,6 +1343,15 @@
                  */
                 case GUEST_MSG_FILTER:
-                    LogFlowFunc(("GUEST_MSG_FILTER\n"));
+                    LogFlowFunc(("[Client %RU32] GUEST_MSG_FILTER\n", u32ClientID));
                     rc = clientSetMsgFilter(u32ClientID, callHandle, cParms, paParms);
+                    break;
+
+                /*
+                 * The guest only wants skip the currently assigned messages.
+                 * Since VBox 4.3+.
+                 */
+                case GUEST_MSG_SKIP:
+                    LogFlowFunc(("[Client %RU32] GUEST_MSG_SKIP\n", u32ClientID));
+                    rc = clientSkipMsg(u32ClientID, callHandle, cParms, paParms);
                     break;
 
@@ -1416,11 +1438,11 @@
  * @copydoc VBOXHGCMSVCLOAD
  */
-extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *ptable)
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
 {
     int rc = VINF_SUCCESS;
 
-    LogFlowFunc(("ptable = %p\n", ptable));
-
-    if (!VALID_PTR(ptable))
+    LogFlowFunc(("pTable=%p\n", pTable));
+
+    if (!VALID_PTR(pTable))
     {
         rc = VERR_INVALID_PARAMETER;
@@ -1428,8 +1450,8 @@
     else
     {
-        LogFlowFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
-
-        if (   ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
-            || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
+        LogFlowFunc(("pTable->cbSize=%d, pTable->u32Version=0x%08X\n", pTable->cbSize, pTable->u32Version));
+
+        if (   pTable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
+            || pTable->u32Version != VBOX_HGCM_SVC_VERSION)
         {
             rc = VERR_VERSION_MISMATCH;
@@ -1440,5 +1462,5 @@
             /* No exceptions may propagate outside. */
             try {
-                apService = std::auto_ptr<Service>(new Service(ptable->pHelpers));
+                apService = std::auto_ptr<Service>(new Service(pTable->pHelpers));
             } catch (int rcThrown) {
                 rc = rcThrown;
@@ -1453,23 +1475,23 @@
                  * because we're a class which can have members for that :-).
                  */
-                ptable->cbClient = 0;
+                pTable->cbClient = 0;
 
                 /* Register functions. */
-                ptable->pfnUnload             = Service::svcUnload;
-                ptable->pfnConnect            = Service::svcConnect;
-                ptable->pfnDisconnect         = Service::svcDisconnect;
-                ptable->pfnCall               = Service::svcCall;
-                ptable->pfnHostCall           = Service::svcHostCall;
-                ptable->pfnSaveState          = NULL;  /* The service is stateless, so the normal */
-                ptable->pfnLoadState          = NULL;  /* construction done before restoring suffices */
-                ptable->pfnRegisterExtension  = Service::svcRegisterExtension;
+                pTable->pfnUnload             = Service::svcUnload;
+                pTable->pfnConnect            = Service::svcConnect;
+                pTable->pfnDisconnect         = Service::svcDisconnect;
+                pTable->pfnCall               = Service::svcCall;
+                pTable->pfnHostCall           = Service::svcHostCall;
+                pTable->pfnSaveState          = NULL;  /* The service is stateless, so the normal */
+                pTable->pfnLoadState          = NULL;  /* construction done before restoring suffices */
+                pTable->pfnRegisterExtension  = Service::svcRegisterExtension;
 
                 /* Service specific initialization. */
-                ptable->pvService = apService.release();
+                pTable->pvService = apService.release();
             }
         }
     }
 
-    LogFlowFunc(("returning %Rrc\n", rc));
+    LogFlowFunc(("Returning %Rrc\n", rc));
     return rc;
 }
Index: /trunk/src/VBox/Main/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Main/Makefile.kmk	(revision 45414)
+++ /trunk/src/VBox/Main/Makefile.kmk	(revision 45415)
@@ -611,4 +611,5 @@
 	src-client/GuestImpl.cpp \
 	src-client/GuestDirectoryImpl.cpp \
+	src-client/GuestErrorInfoImpl.cpp \
 	src-client/GuestFileImpl.cpp \
 	src-client/GuestFsObjInfoImpl.cpp \
Index: /trunk/src/VBox/Main/idl/VirtualBox.xidl
===================================================================
--- /trunk/src/VBox/Main/idl/VirtualBox.xidl	(revision 45414)
+++ /trunk/src/VBox/Main/idl/VirtualBox.xidl	(revision 45415)
@@ -9491,4 +9491,5 @@
       Process execution statuses.
     </desc>
+    
     <const name="Undefined"             value="0">
       <desc>Process is in an undefined state.</desc>
@@ -9528,5 +9529,61 @@
     </const>
   </enum>
-
+  
+  <enum
+    name="ProcessInputStatus"
+    uuid="a4a0ef9c-29cc-4805-9803-c8215ae9da6c"
+    >
+    <desc>
+      Process input statuses.
+    </desc>
+    
+    <const name="Undefined"             value="0">
+      <desc>Undefined state.</desc>
+    </const>
+    <const name="Broken"                value="1">
+      <desc>TODO</desc>
+    </const>
+    <const name="Available"             value="10">
+      <desc>TODO</desc>
+    </const>
+    <const name="Written"               value="50">
+      <desc>TODO</desc>
+    </const>
+    <const name="Overflow"              value="100">
+      <desc>TODO</desc>
+    </const>
+  </enum>
+
+  <enum
+    name="FileStatus"
+    uuid="8c86468b-b97b-4080-8914-e29f5b0abd2c"
+    >
+    <desc>
+      File statuses.
+    </desc>
+    
+    <const name="Undefined"             value="0">
+      <desc>File is in an undefined state.</desc>
+    </const>
+    <const name="Opening"               value="10">
+      <desc>TODO</desc>
+    </const>
+    <const name="Open"                  value="100">
+      <desc>TODO</desc>
+    </const>
+    <const name="Closing"               value="150">
+      <desc>TODO</desc>
+    </const>
+    <const name="Closed"                value="200">
+      <desc>TODO</desc>
+    </const>
+    <const name="Down"                  value="600">
+      <desc>TODO</desc>
+    </const>
+    <const name="Error"                 value="800">
+      <desc>Something went wrong.</desc>
+    </const>
+  </enum>
+  
   <enum
     name="FsObjType"
@@ -9605,8 +9662,27 @@
     </const>
   </enum>
+  
+  <interface
+    name="IGuestErrorInfo" extends="$unknown"
+    uuid="ab576a37-dcfc-4d80-9a73-493d15e293c4"
+    wsmap="managed"
+    >
+    <desc>
+      TODO
+    </desc>
+
+    <attribute name="result" type="long" readonly="yes">
+      <desc>TODO</desc>
+    </attribute>
+    
+    <attribute name="text" type="wstring" readonly="yes">
+      <desc>TODO</desc>
+    </attribute>
+    
+  </interface>
 
   <interface
     name="IGuestSession" extends="$unknown"
-    uuid="56f551a2-f924-43ab-8a69-a954109db878"
+    uuid="c8e8607b-5e67-4073-8f14-146515d0c1ff"
     wsmap="managed"
     >
@@ -9689,4 +9765,10 @@
       </desc>
     </attribute>
+    
+    <attribute name="eventSource" type="IEventSource" readonly="yes">
+      <desc>
+        Event source for guest session events.
+      </desc>
+    </attribute>
 
     <method name="close">
@@ -10706,5 +10788,5 @@
   <interface
     name="IFile" extends="$unknown"
-    uuid="b702a560-6139-4a8e-a892-bbf14b97bf97"
+    uuid="0b45a95f-6267-499e-a7f2-efa5f8e081f2"
     wsmap="managed"
     >
@@ -10712,4 +10794,9 @@
       Abstract parent interface for files handled by VirtualBox.
     </desc>
+    <attribute name="status" type="FileStatus" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>
     <attribute name="creationMode" type="unsigned long" readonly="yes">
       <desc>
@@ -18573,5 +18660,5 @@
   <enum
     name="VBoxEventType"
-    uuid="0d67e79e-b7b1-4919-aab3-b36866075515"
+    uuid="83c03e4b-ffe5-421c-a0fb-0b27b2d56753"
     >
 
@@ -18871,7 +18958,56 @@
       </desc>
     </const>
-
+    <const name="OnGuestSessionStateChanged" value="80">
+      <desc>
+        TODO
+      </desc>
+    </const>
+    <const name="OnGuestSessionRegistered" value="81">
+      <desc>
+        TODO
+      </desc>
+    </const>
+    <const name="OnGuestProcessRegistered" value="82">
+      <desc>
+        TODO
+      </desc>
+    </const>
+    <const name="OnGuestProcessStateChanged" value="83">
+      <desc>
+        TODO
+      </desc>
+    </const>
+    <const name="OnGuestProcessInputNotify" value="84">
+      <desc>
+        TODO
+      </desc>
+    </const>
+    <const name="OnGuestProcessOutput" value="85">
+      <desc>
+        TODO
+      </desc>
+    </const>
+    <const name="OnGuestFileStateChanged" value="86">
+      <desc>
+        TODO
+      </desc>
+    </const>
+    <const name="OnGuestFileOffsetChanged" value="87">
+      <desc>
+        TODO
+      </desc>
+    </const>
+    <const name="OnGuestFileRead" value="88">
+      <desc>
+        For performance reasons ... TODO
+      </desc>
+    </const>
+    <const name="OnGuestFileWrite" value="89">
+      <desc>
+        For performance reasons ... TODO
+      </desc>
+    </const>
     <!-- Last event marker -->
-    <const name="Last" value="80">
+    <const name="Last" value="90">
       <desc>
         Must be last event, used for iterations and structures relying on numerical event values.
@@ -19745,5 +19881,274 @@
   </interface>
 
-
+  <interface
+    name="IGuestSessionEvent" extends="IEvent"
+    uuid="b9acd33f-647d-45ac-8fe9-f49b3183ba37"
+    wsmap="managed" id="GuestSessionEvent"
+    >
+    <desc>Base abstract interface for all guest session events.</desc>
+
+    <attribute name="session" type="IGuestSession" readonly="yes">
+      <desc>Guest session that is subject to change.</desc>
+    </attribute>
+
+  </interface>
+  
+  <interface
+    name="IGuestSessionStateChangedEvent" extends="IGuestSessionEvent"
+    uuid="9c288479-6564-451d-9574-7e7ac0b7e443"
+    wsmap="managed" autogen="VBoxEvent" id="OnGuestSessionStateChanged"
+    >
+    <desc>
+      TODO
+    </desc>
+    
+    <attribute name="id" type="unsigned long" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>
+    <attribute name="status" type="GuestSessionStatus" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>    
+    <attribute name="error" type="IGuestErrorInfo" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>
+  
+  </interface>
+  
+  <interface
+    name="IGuestSessionRegisteredEvent" extends="IGuestSessionEvent"
+    uuid="b79de686-eabd-4fa6-960a-f1756c99ea1c"
+    wsmap="managed" autogen="VBoxEvent" id="OnGuestSessionRegistered"
+    >
+    <desc>
+      TODO
+    </desc>
+    
+    <attribute name="registered" type="boolean" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>
+  
+  </interface>
+  
+  <interface
+    name="IGuestProcessEvent" extends="IGuestSessionEvent"
+    uuid="ee4be6e7-76c5-4517-a623-d4b1957d4ea4"
+    wsmap="managed" id="GuestProcessEvent"
+    >
+    <desc>Base abstract interface for all guest process events.</desc>
+
+    <attribute name="process" type="IGuestProcess" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>
+    <attribute name="pid" type="unsigned long" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>
+
+  </interface>
+  
+  <interface
+    name="IGuestProcessRegisteredEvent" extends="IGuestProcessEvent"
+    uuid="1d89e2b3-c6ea-45b6-9d43-dc6f70cc9f02"
+    wsmap="managed" autogen="VBoxEvent" id="OnGuestProcessRegistered"
+    >
+    <desc>
+      TODO
+    </desc>
+    
+    <attribute name="registered" type="boolean" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>
+  
+  </interface>
+  
+  <interface
+    name="IGuestProcessStateChangedEvent" extends="IGuestProcessEvent"
+    uuid="9360d372-d4d9-4948-8e67-e0a0e603acf9"
+    wsmap="managed" autogen="VBoxEvent" id="OnGuestProcessStateChanged"
+    >
+    <desc>
+      TODO
+    </desc>
+    
+    <attribute name="status" type="ProcessStatus" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>
+    <attribute name="error" type="IGuestErrorInfo" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>
+
+  </interface>
+  
+  <interface
+    name="IGuestProcessInputNotifyEvent" extends="IGuestProcessEvent"
+    uuid="b3d6bb71-0392-4971-a908-f71c931995fd"
+    wsmap="managed" autogen="VBoxEvent" id="OnGuestProcessInputNotify"
+    >
+    <desc>
+      TODO
+    </desc>
+    
+    <attribute name="status" type="ProcessInputStatus" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>
+    
+    <attribute name="handle" type="unsigned long" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>
+    
+    <attribute name="processed" type="unsigned long" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>
+
+  </interface>
+  
+  <interface
+    name="IGuestProcessOutputEvent" extends="IGuestProcessEvent"
+    uuid="a13533b9-4aba-4937-836e-81325859c9d8"
+    wsmap="managed" autogen="VBoxEvent" id="OnGuestProcessOutput"
+    >
+    <desc>
+      TODO
+    </desc>
+    
+    <attribute name="handle" type="unsigned long" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>
+    
+    <attribute name="data" type="octet" safearray="yes" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>
+
+  </interface>
+  
+  <interface
+    name="IGuestFileEvent" extends="IGuestSessionEvent"
+    uuid="912f748f-d2f9-4fea-84d2-d36a017cc5f8"
+    wsmap="managed" id="GuestFileEvent"
+    >
+    <desc>Base abstract interface for all guest file events.</desc>
+
+    <attribute name="file" type="IGuestFile" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>
+
+  </interface>
+  
+  <interface
+    name="IGuestFileStateChangedEvent" extends="IGuestFileEvent"
+    uuid="841951c4-4df3-4ee1-bb99-91e5761c18ff"
+    wsmap="managed" autogen="VBoxEvent" id="OnGuestFileStateChanged"
+    >
+    <desc>
+      TODO
+    </desc>
+    
+    <attribute name="status" type="FileStatus" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>
+    <attribute name="error" type="IGuestErrorInfo" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>
+    <!-- Note: No events for reads/writes for performance reasons.
+               See dedidcated events IGuestFileReadEvent and
+               IGuestFileWriteEvent. -->
+
+  </interface>
+  
+  <interface
+    name="IGuestFileIOEvent" extends="IGuestFileEvent"
+    uuid="b5191a7c-9536-4ef8-820e-3b0e17e5bbc8"
+    wsmap="managed" id="OnGuestFileIO"
+    >
+    <desc>
+      TODO
+    </desc>
+
+    <attribute name="offset" type="long long" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>
+    <attribute name="processed" type="unsigned long" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+      
+    </attribute>
+    
+  </interface>
+  
+  <interface
+    name="IGuestFileOffsetChangedEvent" extends="IGuestFileIOEvent"
+    uuid="e8f79a21-1207-4179-94cf-ca250036308f"
+    wsmap="managed" autogen="VBoxEvent" id="OnGuestFileOffsetChanged"
+    >
+    <desc>
+      TODO
+    </desc>
+
+  </interface>
+  
+  <interface
+    name="IGuestFileReadEvent" extends="IGuestFileIOEvent"
+    uuid="4ee3cbcb-486f-40db-9150-deee3fd24189"
+    wsmap="managed" autogen="VBoxEvent" id="OnGuestFileRead"
+    >
+    <desc>
+      TODO
+    </desc>
+    
+    <attribute name="data" type="octet" safearray="yes" readonly="yes">
+      <desc>
+        TODO
+      </desc>
+    </attribute>
+    
+  </interface>
+  
+  <interface
+    name="IGuestFileWriteEvent" extends="IGuestFileIOEvent"
+    uuid="e062a915-3cf5-4c0a-bc90-9b8d4cc94d89"
+    wsmap="managed" autogen="VBoxEvent" id="OnGuestFileWrite"
+    >
+    <desc>
+      TODO
+    </desc>
+    
+  </interface>
+  
   <interface
     name="IVRDEServerChangedEvent" extends="IEvent"
Index: /trunk/src/VBox/Main/include/GuestCtrlImplPrivate.h
===================================================================
--- /trunk/src/VBox/Main/include/GuestCtrlImplPrivate.h	(revision 45414)
+++ /trunk/src/VBox/Main/include/GuestCtrlImplPrivate.h	(revision 45415)
@@ -132,5 +132,5 @@
     int Init(CALLBACKTYPE enmType);
 
-    CALLBACKTYPE GetCallbackType(void) { return mType; }
+    CALLBACKTYPE GetType(void) { return mType; }
 
     const void* GetDataRaw(void) const { return pvData; }
@@ -568,14 +568,40 @@
 };
 
-/**
- * Pure virtual class (interface) for guest objects (processes, files, ...) --
+class GuestBase
+{
+
+public:
+
+    GuestBase(void);
+    virtual ~GuestBase(void);
+
+public:
+
+    int generateContextID(uint32_t uSessionID, uint32_t uObjectID, uint32_t *puContextID);
+
+protected:
+
+    /** Pointer to the console object. Needed
+     *  for HGCM (VMMDev) communication. */
+    Console                 *mConsole;
+    /** The next upcoming context ID for this object. */
+    uint32_t                 mNextContextID;
+};
+
+/**
+ * Virtual class (interface) for guest objects (processes, files, ...) --
  * contains all per-object callback management.
  */
-class GuestObject
-{
-
-public:
-
-    ULONG getObjectID(void) { return mObject.mObjectID; }
+class GuestObject : public GuestBase
+{
+
+public:
+
+    GuestObject(void);
+    virtual ~GuestObject(void);
+
+public:
+
+    ULONG getObjectID(void) { return mObjectID; }
 
 protected:
@@ -587,9 +613,4 @@
 
     int bindToSession(Console *pConsole, GuestSession *pSession, uint32_t uObjectID);
-    int callbackAdd(GuestCtrlCallback *pCallback, uint32_t *puContextID);
-    void callbackDelete(GuestCtrlCallback *pCallback);
-    bool callbackExists(uint32_t uContextID);
-    int callbackRemove(uint32_t uContextID);
-    int callbackRemoveAll(void);
     int sendCommand(uint32_t uFunction, uint32_t uParms, PVBOXHGCMSVCPARM paParms);
 
@@ -597,223 +618,20 @@
 
     /**
-     * Commom structure for all derived objects, when then have
+     * Commom parameters for all derived objects, when then have
      * an own mData structure to keep their specific data around.
      */
-    struct Object
-    {
-        /** Pointer to parent session. Per definition
-         *  this objects *always* lives shorter than the
-         *  parent. */
-        GuestSession            *mSession;
-        /** Pointer to the console object. Needed
-         *  for HGCM (VMMDev) communication. */
-        Console                 *mConsole;
-        /** All related callbacks to this object. */
-        GuestCtrlCallbacks       mCallbacks;
-        /** The next upcoming context ID for this object. */
-        ULONG                    mNextContextID;
-        /** The object ID -- must be unique for each guest
-         *  session and is encoded into the context ID. Must
-         *  be set manually when initializing the object.
-         *
-         *  For guest processes this is the internal PID,
-         *  for guest files this is the internal file ID. */
-        uint32_t                 mObjectID;
-    } mObject;
-};
-
-#if 0
-/*
- * Guest (HGCM) callbacks. All classes will throw
- * an exception on misuse.
- */
-
-/** Callback class for guest process status. */
-class GuestCbProcessStatus : public GuestCtrlCallback
-{
-
-public:
-
-    int Init(uint32_t uProtocol, uint32_t uFunction,
-             PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
-    {
-        AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
-
-        int rc = GuestCtrlCallback::Init();
-        if (RT_FAILURE(rc))
-            return rc;
-
-        if (   uFunction != GUEST_EXEC_SEND_STATUS
-            || pSvcCb->mParms < 5)
-            return VERR_INVALID_PARAMETER;
-
-        /* pSvcCb->mpaParms[0] always contains the context ID. */
-        pSvcCb->mpaParms[1].getUInt32(&mPID);
-        pSvcCb->mpaParms[2].getUInt32(&mStatus);
-        pSvcCb->mpaParms[3].getUInt32(&mFlags); /* Can contain an IPRT error, which is a signed int. */
-        pSvcCb->mpaParms[4].getPointer(&mData, &mcbData);
-
-        return VINF_SUCCESS;
-    }
-
-    void Destroy(void) { }
-
-    uint32_t  mPID;
-    uint32_t  mStatus;
-    uint32_t  mFlags;
-    void     *mData;
-    uint32_t  mcbData;
-};
-
-/** Callback class for guest process input. */
-class GuestCbProcessInput : public GuestCtrlCallback
-{
-
-public:
-
-    int Init(uint32_t uProtocol, uint32_t uFunction,
-             PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
-    {
-        AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
-
-        int rc = GuestCtrlCallback::Init();
-        if (RT_FAILURE(rc))
-            return rc;
-
-        if (   uFunction != GUEST_EXEC_SEND_INPUT_STATUS
-            || pSvcCb->mParms < 5)
-            return VERR_INVALID_PARAMETER;
-
-        /* pSvcCb->mpaParms[0] always contains the context ID. */
-        pSvcCb->mpaParms[1].getUInt32(&mPID);
-        /* Associated file handle. */
-        pSvcCb->mpaParms[2].getUInt32(&mStatus);
-        pSvcCb->mpaParms[3].getUInt32(&mFlags);
-        pSvcCb->mpaParms[4].getUInt32(&mProcessed);
-
-        return VINF_SUCCESS;
-    }
-
-    void Destroy(void) { }
-
-    GuestCbProcessInput& operator=(const GuestCbProcessInput &that)
-    {
-        mPID = that.mPID;
-        mStatus = that.mStatus;
-        mFlags = that.mFlags;
-        mProcessed = that.mProcessed;
-        return *this;
-    }
-
-    uint32_t  mPID;
-    uint32_t  mStatus;
-    uint32_t  mFlags;
-    uint32_t  mProcessed;
-};
-
-/** Callback class for guest process output. */
-class GuestCbProcessOutput : public GuestCtrlCallback
-{
-
-public:
-
-    int Init(uint32_t uProtocol, uint32_t uFunction,
-             PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
-    {
-        AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
-
-        int rc = GuestCtrlCallback::Init();
-        if (RT_FAILURE(rc))
-            return rc;
-
-        if (   uFunction != GUEST_EXEC_SEND_OUTPUT
-            || pSvcCb->mParms < 5)
-            return VERR_INVALID_PARAMETER;
-
-        /* pSvcCb->mpaParms[0] always contains the context ID. */
-        pSvcCb->mpaParms[1].getUInt32(&mPID);
-        /* Associated file handle. */
-        pSvcCb->mpaParms[2].getUInt32(&mHandle);
-        pSvcCb->mpaParms[3].getUInt32(&mFlags);
-
-        void *pbData; uint32_t cbData;
-        rc = pSvcCb->mpaParms[4].getPointer(&pbData, &cbData);
-        if (RT_SUCCESS(rc))
-        {
-            Assert(cbData);
-            mData = RTMemAlloc(cbData);
-            AssertPtrReturn(mData, VERR_NO_MEMORY);
-            memcpy(mData, pbData, cbData);
-            mcbData = cbData;
-        }
-
-        return rc;
-    }
-
-    void Destroy(void)
-    {
-        if (mData)
-        {
-            RTMemFree(mData);
-            mData = NULL;
-            mcbData = 0;
-        }
-    }
-
-    GuestCbProcessOutput& operator=(const GuestCbProcessOutput &that)
-    {
-        mPID = that.mPID;
-        mHandle = that.mHandle;
-        mFlags = that.mFlags;
-
-        Destroy();
-        if (that.mcbData)
-        {
-            void *pvData = RTMemAlloc(that.mcbData);
-            if (pvData)
-            {
-                AssertPtr(pvData);
-                memcpy(pvData, that.mData, that.mcbData);
-                mData = pvData;
-                mcbData = that.mcbData;
-            }
-        }
-
-        return *this;
-    }
-
-    uint32_t  mPID;
-    uint32_t  mHandle;
-    uint32_t  mFlags;
-    void     *mData;
-    size_t    mcbData;
-};
-
-/** Callback class for guest process IO notifications. */
-class GuestCbProcessIO : public GuestCtrlCallback
-{
-
-public:
-
-    int Init(uint32_t uProtocol, uint32_t uFunction,
-             PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
-    {
-        AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
-
-        int rc = GuestCtrlCallback::Init();
-        if (RT_FAILURE(rc))
-            return rc;
-
-        return VERR_NOT_IMPLEMENTED;
-    }
-
-    void Destroy(void) { GuestCtrlCallback::Destroy(); }
-
-    GuestCbProcessIO& operator=(const GuestCbProcessIO &that)
-    {
-        return *this;
-    }
-};
-#endif
+
+    /** Pointer to parent session. Per definition
+     *  this objects *always* lives shorter than the
+     *  parent. */
+    GuestSession            *mSession;
+    /** The object ID -- must be unique for each guest
+     *  object and is encoded into the context ID. Must
+     *  be set manually when initializing the object.
+     *
+     *  For guest processes this is the internal PID,
+     *  for guest files this is the internal file ID. */
+    uint32_t                 mObjectID;
+};
 #endif // ____H_GUESTIMPLPRIVATE
 
Index: /trunk/src/VBox/Main/include/GuestErrorInfoImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/GuestErrorInfoImpl.h	(revision 45415)
+++ /trunk/src/VBox/Main/include/GuestErrorInfoImpl.h	(revision 45415)
@@ -0,0 +1,73 @@
+
+/* $Id$  */
+/** @file
+ * VirtualBox Main - Guest error information.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef ____H_GUESTERRORINFOIMPL
+#define ____H_GUESTERRORINFOIMPL
+
+#include "VirtualBoxBase.h"
+#include "VirtualBoxErrorInfoImpl.h"
+
+/**
+ * TODO
+ */
+class ATL_NO_VTABLE GuestErrorInfo :
+    public VirtualBoxBase,
+    VBOX_SCRIPTABLE_IMPL(IGuestErrorInfo)
+{
+public:
+    /** @name COM and internal init/term/mapping cruft.
+     * @{ */
+    VIRTUALBOXBASE_ADD_ERRORINFO_SUPPORT(GuestErrorInfo, IGuestErrorInfo)
+    DECLARE_NOT_AGGREGATABLE(GuestErrorInfo)
+    DECLARE_PROTECT_FINAL_CONSTRUCT()
+    BEGIN_COM_MAP(GuestErrorInfo)
+        COM_INTERFACE_ENTRY(IGuestErrorInfo)
+    END_COM_MAP()
+    DECLARE_EMPTY_CTOR_DTOR(GuestErrorInfo)
+
+    int     init(LONG uResult, const Utf8Str &strMessage);
+
+    HRESULT FinalConstruct(void);
+    void    FinalRelease(void);
+    /** @}  */
+
+    /** @name IGuestErrorInfo interface.
+     * @{ */
+    STDMETHOD(COMGETTER(Result))(LONG *aResult);
+    STDMETHOD(COMGETTER(Text))(BSTR *aText);
+    /** @}  */
+
+public:
+    /** @name Public internal methods.
+     * @{ */
+    /** @}  */
+
+private:
+    /** @name Private internal methods.
+     * @{ */
+    /** @}  */
+
+    struct Data
+    {
+        ULONG   mResult;
+        Utf8Str mText;
+    } mData;
+};
+
+#endif /* !____H_GUESTERRORINFOIMPL */
+
Index: /trunk/src/VBox/Main/include/GuestFileImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/GuestFileImpl.h	(revision 45414)
+++ /trunk/src/VBox/Main/include/GuestFileImpl.h	(revision 45415)
@@ -21,4 +21,5 @@
 
 #include "VirtualBoxBase.h"
+#include "EventImpl.h"
 
 #include "GuestFsObjInfoImpl.h"
@@ -63,4 +64,5 @@
     STDMETHOD(COMGETTER(Offset))(LONG64 *aOffset);
     STDMETHOD(COMGETTER(OpenMode))(ULONG *aOpenMode);
+    STDMETHOD(COMGETTER(Status))(FileStatus_T *aStatus);
 
     STDMETHOD(Close)(void);
@@ -80,26 +82,36 @@
     int             closeFile(int *pGuestRc);
     static uint32_t getDispositionFromString(const Utf8Str &strDisposition);
+    EventSource    *getEventSource(void) { return mEventSource; }
     static uint32_t getOpenModeFromString(const Utf8Str &strOpenMode);
     static Utf8Str  guestErrorToString(int guestRc);
-    int             onFileNotify(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData, GuestCtrlCallback *pCallback);
-    int             onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData, GuestCtrlCallback *pCallback);
+    int             onFileNotify(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData);
+    int             onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData);
     int             openFile(int *pGuestRc);
-    int             readData(uint32_t uSize, uint32_t uTimeoutMS, void *pvData, size_t cbData, size_t *pcbRead, int *pGuestRc);
-    int             readDataAt(uint64_t uOffset, uint32_t uSize, uint32_t uTimeoutMS, void *pvData, size_t cbData, size_t *pcbRead, int *pGuestRc);
-    int             seekAt(uint64_t uOffset, GUEST_FILE_SEEKTYPE eSeekType, uint32_t uTimeoutMS, int *pGuestRc);
-    int             sendFileCommand(uint32_t uFunction, uint32_t uParms, PVBOXHGCMSVCPARM paParms, uint32_t uTimeoutMS, int *pGuestRc, GuestCtrlCallback **ppCallback);
+    int             readData(uint32_t uSize, uint32_t uTimeoutMS, void* pvData, uint32_t cbData, uint32_t* pcbRead);
+    int             readDataAt(uint64_t uOffset, uint32_t uSize, uint32_t uTimeoutMS, void* pvData, size_t cbData, size_t* pcbRead);
+    int             seekAt(uint64_t uOffset, GUEST_FILE_SEEKTYPE eSeekType, uint32_t uTimeoutMS, uint64_t *puOffset);
     static HRESULT  setErrorExternal(VirtualBoxBase *pInterface, int guestRc);
-    int             writeData(uint32_t uTimeoutMS, void *pvData, size_t cbData, uint32_t *pcbWritten, int *pGuestRc);
-    int             writeDataAt(uint64_t uOffset, uint32_t uTimeoutMS, void *pvData, size_t cbData, uint32_t *pcbWritten, int *pGuestRc);
+    int             setFileStatus(FileStatus_T fileStatus, int fileRc);
+    int             waitForOffsetChange(uint32_t uTimeoutMS, uint64_t *puOffset);
+    int             waitForRead(uint32_t uTimeoutMS, void* pvData, size_t cbData, uint32_t *pcbRead);
+    int             waitForStatusChange(uint32_t uTimeoutMS, FileStatus_T *pFileStatus);
+    int             waitForWrite(uint32_t uTimeoutMS, uint32_t *pcbWritten);
+    int             writeData(uint32_t uTimeoutMS, void *pvData, uint32_t cbData, uint32_t *pcbWritten);
+    int             writeDataAt(uint64_t uOffset, uint32_t uTimeoutMS, void *pvData, uint32_t cbData, uint32_t *pcbWritten);
     /** @}  */
 
 private:
 
+    /** The internal console object. */
+    Console                *mConsole;
+    /** The associate session this file belongs to. */
+    GuestSession           *mSession;
+    /** This can safely be used without holding any locks.
+     * An AutoCaller suffices to prevent it being destroy while in use and
+     * internally there is a lock providing the necessary serialization. */
+    const ComObjPtr<EventSource> mEventSource;
+
     struct Data
     {
-        /** The internal console object. */
-        Console                *mConsole;
-        /** The associate session this file belongs to. */
-        GuestSession           *mSession;
         /** All related callbacks to this file. */
         GuestCtrlCallbacks      mCallbacks;
@@ -110,4 +122,6 @@
         /** The file's internal ID. */
         uint32_t                mID;
+        /** The current file status. */
+        FileStatus_T            mStatus;
         /** The file's current offset. */
         uint64_t                mOffCurrent;
Index: /trunk/src/VBox/Main/include/GuestImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/GuestImpl.h	(revision 45414)
+++ /trunk/src/VBox/Main/include/GuestImpl.h	(revision 45415)
@@ -177,22 +177,27 @@
         GuestSessions           mGuestSessions;
         uint32_t                mNextSessionID;
-    };
-
-    ULONG             mMemoryBalloonSize;
-    ULONG             mStatUpdateInterval;
-    uint64_t          mNetStatRx;
-    uint64_t          mNetStatTx;
-    uint64_t          mNetStatLastTs;
-    ULONG             mCurrentGuestStat[GUESTSTATTYPE_MAX];
-    ULONG             mVmValidStats;
-    BOOL              mCollectVMMStats;
-    BOOL              mfPageFusionEnabled;
-
-    Console *mParent;
-    Data mData;
-
-#ifdef VBOX_WITH_GUEST_CONTROL
+    } mData;
+
+    ULONG                           mMemoryBalloonSize;
+    ULONG                           mStatUpdateInterval;
+    uint64_t                        mNetStatRx;
+    uint64_t                        mNetStatTx;
+    uint64_t                        mNetStatLastTs;
+    ULONG                           mCurrentGuestStat[GUESTSTATTYPE_MAX];
+    ULONG                           mVmValidStats;
+    BOOL                            mCollectVMMStats;
+    BOOL                            mfPageFusionEnabled;
+
+    Console                        *mParent;
+
+#ifdef VBOX_WITH_GUEST_CONTROL
+    /**
+     * This can safely be used without holding any locks.
+     * An AutoCaller suffices to prevent it being destroy while in use and
+     * internally there is a lock providing the necessary serialization.
+     */
+    const ComObjPtr<EventSource>    mEventSource;
     /** General extension callback for guest control. */
-    HGCMSVCEXTHANDLE  mhExtCtrl;
+    HGCMSVCEXTHANDLE                mhExtCtrl;
 #endif
 
Index: /trunk/src/VBox/Main/include/GuestProcessImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/GuestProcessImpl.h	(revision 45414)
+++ /trunk/src/VBox/Main/include/GuestProcessImpl.h	(revision 45415)
@@ -83,4 +83,7 @@
     int terminateProcess(int *pGuestRc);
     int waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, ProcessWaitResult_T &waitResult, int *pGuestRc);
+    int waitForInputNotify(uint32_t uHandle, uint32_t uTimeoutMS, ProcessInputStatus_T *pInputStatus, size_t *pcbProcessed);
+    int waitForOutput(uint32_t uHandle, uint32_t uTimeoutMS, void* pvData, size_t cbData, size_t* pcbRead);
+    int waitForStatusChange(uint32_t fWaitFlags, uint32_t uTimeoutMS, ProcessStatus_T *pProcessStatus, int *pGuestRc);
     int writeData(uint32_t uHandle, uint32_t uFlags, void *pvData, size_t cbData, uint32_t uTimeoutMS, uint32_t *puWritten, int *pGuestRc);
     /** @}  */
@@ -90,16 +93,22 @@
      * @{ */
     inline bool isAlive(void);
-    int onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, GuestCtrlCallback *pCallback, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData);
-    int onProcessInputStatus(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, GuestCtrlCallback *pCallback, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData);
-    int onProcessNotifyIO(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, GuestCtrlCallback * pCallback, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData);
-    int onProcessStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, GuestCtrlCallback *pCallback, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData);
-    int onProcessOutput(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, GuestCtrlCallback *pCallback, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData);
+    int onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData);
+    int onProcessInputStatus(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData);
+    int onProcessNotifyIO(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData);
+    int onProcessStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData);
+    int onProcessOutput(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData);
     int prepareExecuteEnv(const char *pszEnv, void **ppvList, ULONG *pcbList, ULONG *pcEnvVars);
     int setProcessStatus(ProcessStatus_T procStatus, int procRc);
-    int signalWaiters(ProcessWaitResult_T enmWaitResult, int rc = VINF_SUCCESS);
     static DECLCALLBACK(int) startProcessThread(RTTHREAD Thread, void *pvUser);
     /** @}  */
 
 private:
+
+    /**
+     * This can safely be used without holding any locks.
+     * An AutoCaller suffices to prevent it being destroy while in use and
+     * internally there is a lock providing the necessary serialization.
+     */
+    const ComObjPtr<EventSource> mEventSource;
 
     struct Data
@@ -116,10 +125,4 @@
          *  returned from the guest side. */
         int                      mRC;
-        /** How many waiters? At the moment there can only
-         *  be one. */
-        uint32_t                 mWaitCount;
-        /** The actual process event for doing the waits.
-         *  At the moment we only support one wait a time. */
-        GuestProcessWaitEvent   *mWaitEvent;
     } mData;
 };
Index: /trunk/src/VBox/Main/include/GuestSessionImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/GuestSessionImpl.h	(revision 45414)
+++ /trunk/src/VBox/Main/include/GuestSessionImpl.h	(revision 45415)
@@ -21,4 +21,5 @@
 
 #include "VirtualBoxBase.h"
+#include "EventImpl.h"
 
 #include "GuestCtrlImplPrivate.h"
@@ -238,4 +239,5 @@
 class ATL_NO_VTABLE GuestSession :
     public VirtualBoxBase,
+    public GuestBase,
     VBOX_SCRIPTABLE_IMPL(IGuestSession)
 {
@@ -271,4 +273,5 @@
     STDMETHOD(COMGETTER(Directories))(ComSafeArrayOut(IGuestDirectory *, aDirectories));
     STDMETHOD(COMGETTER(Files))(ComSafeArrayOut(IGuestFile *, aFiles));
+    STDMETHOD(COMGETTER(EventSource))(IEventSource ** aEventSource);
     /** @}  */
 
@@ -345,13 +348,15 @@
     const GuestCredentials &getCredentials(void);
     const GuestEnvironment &getEnvironment(void);
+    EventSource            *getEventSource(void) { return mEventSource; }
     Utf8Str                 getName(void);
     ULONG                   getId(void) { return mData.mSession.mID; }
     static Utf8Str          guestErrorToString(int guestRc);
-    int                     onSessionStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, GuestCtrlCallback *pCallback, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData);
+    HRESULT                 isReadyExternal(void);
+    int                     onSessionStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData);
     int                     startSessionIntenal(int *pGuestRc);
     int                     startSessionAsync(void);
     static DECLCALLBACK(int)
                             startSessionThread(RTTHREAD Thread, void *pvUser);
-    Guest                  *getParent(void) { return mData.mParent; }
+    Guest                  *getParent(void) { return mParent; }
     uint32_t                getProtocolVersion(void) { return mData.mProtocolVersion; }
     int                     processRemoveFromList(GuestProcess *pProcess);
@@ -361,15 +366,25 @@
     int                     sendCommand(uint32_t uFunction, uint32_t uParms, PVBOXHGCMSVCPARM paParms);
     static HRESULT          setErrorExternal(VirtualBoxBase *pInterface, int guestRc);
+    int                     setSessionStatus(GuestSessionStatus_T sessionStatus, int sessionRc);
+    int                     signalWaiters(GuestSessionWaitResult_T enmWaitResult, int rc /*= VINF_SUCCESS */);
     int                     startTaskAsync(const Utf8Str &strTaskDesc, GuestSessionTask *pTask, ComObjPtr<Progress> &pProgress);
     int                     queryInfo(void);
     int                     waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestSessionWaitResult_T &waitResult, int *pGuestRc);
+    int                     waitForStateChange(uint32_t fWaitFlags, uint32_t uTimeoutMS, GuestSessionStatus_T *pSessionStatus, int *pGuestRc);
     /** @}  */
 
 private:
+
+    /** Pointer to the parent (Guest). */
+    Guest                          *mParent;
+    /**
+     * This can safely be used without holding any locks.
+     * An AutoCaller suffices to prevent it being destroy while in use and
+     * internally there is a lock providing the necessary serialization.
+     */
+    const ComObjPtr<EventSource>    mEventSource;
 
     struct Data
     {
-        /** Pointer to the parent (Guest). */
-        Guest                      *mParent;
         /** The session credentials. */
         GuestCredentials            mCredentials;
@@ -381,7 +396,4 @@
          *  overwritten/extended by ProcessCreate(Ex). */
         GuestEnvironment            mEnvironment;
-        /** The session callback, needed for communicating
-         *  with the guest. */
-        GuestCtrlCallback           mCallback;
         /** Directory objects bound to this session. */
         SessionDirectories          mDirectories;
@@ -402,10 +414,4 @@
          *  returned from the guest side. */
         int                         mRC;
-        /** How many waiters? At the moment there can only
-         *  be one. */
-        uint32_t                    mWaitCount;
-        /** The actual session event for doing the waits.
-         *  At the moment we only support one wait a time. */
-        GuestSessionWaitEvent      *mWaitEvent;
     } mData;
 };
Index: /trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp	(revision 45414)
+++ /trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp	(revision 45415)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2006-2012 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -23,4 +23,5 @@
 #include "ConsoleImpl.h"
 #include "ProgressImpl.h"
+#include "VBoxEvents.h"
 #include "VMMDev.h"
 
@@ -159,5 +160,5 @@
         {
             case VERR_MAX_PROCS_REACHED:
-                hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of guest sessions (%ld) reached"),
+                hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of concurrent guest sessions (%ld) reached"),
                               VBOX_GUESTCTRL_MAX_SESSIONS);
                 break;
@@ -316,6 +317,6 @@
     LogFlowFunc(("Closing session (ID=%RU32) ...\n", pSession->getId()));
 
-    for (GuestSessions::iterator itSessions = mData.mGuestSessions.begin();
-         itSessions != mData.mGuestSessions.end(); ++itSessions)
+    GuestSessions::iterator itSessions = mData.mGuestSessions.begin();
+    while (itSessions != mData.mGuestSessions.end())
     {
         if (pSession == itSessions->second)
@@ -324,9 +325,13 @@
                          (GuestSession *)itSessions->second, itSessions->second->getId(), mData.mGuestSessions.size() - 1));
 
-            mData.mGuestSessions.erase(itSessions++);
-
+            mData.mGuestSessions.erase(itSessions);
+
+            fireGuestSessionRegisteredEvent(mEventSource, pSession,
+                                            false /* Unregistered */);
             rc = VINF_SUCCESS;
             break;
         }
+
+        itSessions++;
     }
 
@@ -398,11 +403,10 @@
          * Add session object to our session map. This is necessary
          * before calling openSession because the guest calls back
-         * with the creation result to this session.
+         * with the creation result of this session.
          */
         mData.mGuestSessions[uNewSessionID] = pGuestSession;
 
-        /* Drop write lock before opening session, because this will
-         * involve the main dispatcher to run. */
-        alock.release();
+        fireGuestSessionRegisteredEvent(mEventSource, pGuestSession,
+                                        true /* Registered */);
     }
     catch (int rc2)
@@ -459,19 +463,9 @@
     }
 
-    int guestRc;
     if (RT_SUCCESS(rc))
     {
-        /** @todo Do we need to use openSessioAsync() here? Otherwise
-         *        there might be a problem on webservice calls (= timeouts)
-         *        if opening the guest session takes too long -> Use
-         *        the new session.getStatus() API call! */
-
-        /* Start (fork) the session on the guest. */
-        rc = pSession->startSessionIntenal(&guestRc);
-        if (RT_SUCCESS(rc))
-        {
-            LogFlowFunc(("Created new guest session (pSession=%p), now %zu sessions total\n",
-                         (GuestSession *)pSession, mData.mGuestSessions.size()));
-        }
+        /* Start (fork) the session asynchronously
+         * on the guest. */
+        rc = pSession->startSessionAsync();
     }
 
@@ -482,10 +476,6 @@
         switch (rc)
         {
-            case VERR_GSTCTL_GUEST_ERROR:
-                hr = GuestSession::setErrorExternal(this, guestRc);
-                break;
-
             case VERR_MAX_PROCS_REACHED:
-                hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of guest sessions (%ld) reached"),
+                hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of concurrent guest sessions (%ld) reached"),
                               VBOX_GUESTCTRL_MAX_SESSIONS);
                 break;
Index: /trunk/src/VBox/Main/src-client/GuestCtrlPrivate.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/GuestCtrlPrivate.cpp	(revision 45414)
+++ /trunk/src/VBox/Main/src-client/GuestCtrlPrivate.cpp	(revision 45415)
@@ -1258,4 +1258,38 @@
 }
 
+GuestBase::GuestBase(void)
+    : mConsole(NULL),
+      mNextContextID(0)
+{
+}
+
+GuestBase::~GuestBase(void)
+{
+}
+
+int GuestBase::generateContextID(uint32_t uSessionID, uint32_t uObjectID, uint32_t *puContextID)
+{
+    AssertPtrReturn(puContextID, VERR_INVALID_POINTER);
+
+    uint32_t uCount = mNextContextID++;
+    if (uCount == VBOX_GUESTCTRL_MAX_CONTEXTS)
+        uCount = 0;
+
+    uint32_t uNewContextID =
+        VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID, uObjectID, uCount);
+
+    *puContextID = uNewContextID;
+}
+
+GuestObject::GuestObject(void)
+    : mSession(NULL),
+      mObjectID(0)
+{
+}
+
+GuestObject::~GuestObject(void)
+{
+}
+
 int GuestObject::bindToSession(Console *pConsole, GuestSession *pSession, uint32_t uObjectID)
 {
@@ -1263,125 +1297,9 @@
     AssertPtrReturn(pSession, VERR_INVALID_POINTER);
 
-    mObject.mConsole = pConsole;
-    mObject.mSession = pSession;
-
-    mObject.mNextContextID = 0;
-    mObject.mObjectID = uObjectID;
+    mConsole  = pConsole;
+    mSession  = pSession;
+    mObjectID = uObjectID;
 
     return VINF_SUCCESS;
-}
-
-int GuestObject::callbackAdd(GuestCtrlCallback *pCallback, uint32_t *puContextID)
-{
-    const ComObjPtr<GuestSession> pSession(mObject.mSession);
-    Assert(!pSession.isNull());
-    ULONG uSessionID = 0;
-    pSession->COMGETTER(Id)(&uSessionID);
-
-    /* Create a new context ID and assign it. */
-    int vrc = VERR_NOT_FOUND;
-
-    ULONG uCount = mObject.mNextContextID++;
-    ULONG uNewContextID = 0;
-    ULONG uTries = 0;
-    for (;;)
-    {
-        if (uCount == VBOX_GUESTCTRL_MAX_CONTEXTS)
-            uCount = 0;
-
-        /* Create a new context ID ... */
-        uNewContextID = VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID, mObject.mObjectID, uCount);
-
-        /* Is the context ID already used?  Try next ID ... */
-        if (!callbackExists(uCount))
-        {
-            /* Callback with context ID was not found. This means
-             * we can use this context ID for our new callback we want
-             * to add below. */
-            vrc = VINF_SUCCESS;
-            break;
-        }
-
-        uCount++;
-        if (++uTries == UINT32_MAX)
-            break; /* Don't try too hard. */
-    }
-
-    if (RT_SUCCESS(vrc))
-    {
-        /* Add callback with new context ID to our callback map.
-         * Note: This is *not* uNewContextID (which also includes
-         *       the session + process ID), just the context count
-         *       will be used here. */
-        mObject.mCallbacks[uCount] = pCallback;
-        Assert(mObject.mCallbacks.size());
-
-        /* Report back new context ID. */
-        if (puContextID)
-            *puContextID = uNewContextID;
-
-        LogFlowThisFunc(("Added new callback (Session: %RU32, Object: %RU32, Count: %RU32) CID=%RU32\n",
-                         uSessionID, mObject.mObjectID, uCount, uNewContextID));
-    }
-
-    return vrc;
-}
-
-void GuestObject::callbackDelete(GuestCtrlCallback *pCallback)
-{
-    if (pCallback)
-    {
-        delete pCallback;
-        pCallback = NULL;
-    }
-}
-
-bool GuestObject::callbackExists(uint32_t uContextID)
-{
-    GuestCtrlCallbacks::const_iterator it =
-        mObject.mCallbacks.find(VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID));
-    return (it == mObject.mCallbacks.end()) ? false : true;
-}
-
-int GuestObject::callbackRemove(uint32_t uContextID)
-{
-    LogFlowThisFunc(("Removing callback (Session=%RU32, Object=%RU32, Count=%RU32) CID=%RU32\n",
-                     VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uContextID),
-                     VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(uContextID),
-                     VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID),
-                     uContextID));
-
-    GuestCtrlCallbacks::iterator it =
-        mObject.mCallbacks.find(VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID));
-    if (it != mObject.mCallbacks.end())
-    {
-        mObject.mCallbacks.erase(it);
-
-        return VINF_SUCCESS;
-    }
-
-    return VERR_NOT_FOUND;
-}
-
-int GuestObject::callbackRemoveAll(void)
-{
-    int vrc = VINF_SUCCESS;
-
-    /*
-     * Cancel all callbacks + waiters.
-     * Note: Deleting them is the job of the caller!
-     */
-    for (GuestCtrlCallbacks::iterator itCallbacks = mObject.mCallbacks.begin();
-         itCallbacks != mObject.mCallbacks.end(); ++itCallbacks)
-    {
-        GuestCtrlCallback *pCallback = itCallbacks->second;
-        AssertPtr(pCallback);
-        int rc2 = pCallback->Cancel();
-        if (RT_SUCCESS(vrc))
-            vrc = rc2;
-    }
-    mObject.mCallbacks.clear();
-
-    return vrc;
 }
 
@@ -1392,5 +1310,5 @@
 
 #ifndef VBOX_GUESTCTRL_TEST_CASE
-    ComObjPtr<Console> pConsole = mObject.mConsole;
+    ComObjPtr<Console> pConsole = mConsole;
     Assert(!pConsole.isNull());
 
Index: /trunk/src/VBox/Main/src-client/GuestErrorInfoImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/GuestErrorInfoImpl.cpp	(revision 45415)
+++ /trunk/src/VBox/Main/src-client/GuestErrorInfoImpl.cpp	(revision 45415)
@@ -0,0 +1,92 @@
+
+/* $Id$ */
+/** @file
+ * VirtualBox Main - Guest error information.
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include "GuestErrorInfoImpl.h"
+
+#include "Global.h"
+#include "AutoCaller.h"
+
+#ifdef LOG_GROUP
+ #undef LOG_GROUP
+#endif
+#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
+#include <VBox/log.h>
+
+
+// constructor / destructor
+/////////////////////////////////////////////////////////////////////////////
+
+DEFINE_EMPTY_CTOR_DTOR(GuestErrorInfo)
+
+HRESULT GuestErrorInfo::FinalConstruct(void)
+{
+    LogFlowThisFunc(("\n"));
+    return BaseFinalConstruct();
+}
+
+void GuestErrorInfo::FinalRelease(void)
+{
+    LogFlowThisFuncEnter();
+    BaseFinalRelease();
+    LogFlowThisFuncLeave();
+}
+
+// public initializer/uninitializer for internal purposes only
+/////////////////////////////////////////////////////////////////////////////
+
+int GuestErrorInfo::init(LONG uResult, const Utf8Str &strText)
+{
+    mData.mResult = uResult;
+    mData.mText = strText;
+
+    return VINF_SUCCESS;
+}
+
+// implementation of public getters/setters for attributes
+/////////////////////////////////////////////////////////////////////////////
+
+STDMETHODIMP GuestErrorInfo::COMGETTER(Result)(LONG *aResult)
+{
+    LogFlowThisFuncEnter();
+
+    CheckComArgOutPointerValid(aResult);
+
+    *aResult = mData.mResult;
+    return S_OK;
+}
+
+STDMETHODIMP GuestErrorInfo::COMGETTER(Text)(BSTR *aText)
+{
+    LogFlowThisFuncEnter();
+
+    CheckComArgOutPointerValid(aText);
+
+    mData.mText.cloneTo(aText);
+    return S_OK;
+}
+
+// private methods
+/////////////////////////////////////////////////////////////////////////////
+
+// implementation of public methods
+/////////////////////////////////////////////////////////////////////////////
+
Index: /trunk/src/VBox/Main/src-client/GuestFileImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/GuestFileImpl.cpp	(revision 45414)
+++ /trunk/src/VBox/Main/src-client/GuestFileImpl.cpp	(revision 45415)
@@ -21,4 +21,5 @@
 *   Header Files                                                               *
 *******************************************************************************/
+#include "GuestErrorInfoImpl.h"
 #include "GuestFileImpl.h"
 #include "GuestSessionImpl.h"
@@ -28,5 +29,7 @@
 #include "Global.h"
 #include "AutoCaller.h"
-
+#include "VBoxEvents.h"
+
+#include <iprt/cpp/utils.h> /* For unconst(). */
 #include <iprt/file.h>
 #include <VBox/com/array.h>
@@ -88,11 +91,21 @@
         mData.mID = 0;
         mData.mInitialSize = 0;
-
+        mData.mStatus = FileStatus_Undefined;
+
+        unconst(mEventSource).createObject();
+        HRESULT hr = mEventSource->init(static_cast<IGuestFile*>(this));
+        if (FAILED(hr))
+            vrc = VERR_COM_UNEXPECTED;
+    }
+
+    if (RT_SUCCESS(vrc))
+    {
         /* Confirm a successful initialization when it's the case. */
         autoInitSpan.setSucceeded();
-        return vrc;
-    }
-
-    autoInitSpan.setFailed();
+    }
+    else
+        autoInitSpan.setFailed();
+
+    LogFlowFuncLeaveRC(vrc);
     return vrc;
 }
@@ -112,9 +125,5 @@
 
 #ifdef VBOX_WITH_GUEST_CONTROL
-    /*
-     * Cancel + remove all callbacks + waiters.
-     * Note: Deleting them is the job of the caller!
-     */
-    callbackRemoveAll();
+    unconst(mEventSource).setNull();
 #endif
 
@@ -230,4 +239,22 @@
 
     *aOpenMode = getOpenModeFromString(mData.mOpenInfo.mOpenMode);
+
+    return S_OK;
+#endif /* VBOX_WITH_GUEST_CONTROL */
+}
+
+STDMETHODIMP GuestFile::COMGETTER(Status)(FileStatus_T *aStatus)
+{
+#ifndef VBOX_WITH_GUEST_CONTROL
+    ReturnComNotImplemented();
+#else
+    LogFlowThisFuncEnter();
+
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    *aStatus = mData.mStatus;
 
     return S_OK;
@@ -248,29 +275,13 @@
     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
-    /* Get the optional callback associated to this context ID.
-     * The callback may not be around anymore if just kept locally by the caller when
-     * doing the actual HGCM sending stuff. */
-    GuestCtrlCallback *pCallback = NULL;
-    GuestCtrlCallbacks::const_iterator it
-        = mData.mCallbacks.find(VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(pCbCtx->uContextID));
-    if (it != mData.mCallbacks.end())
-    {
-        pCallback = it->second;
-        AssertPtr(pCallback);
-#ifdef DEBUG
-        LogFlowThisFunc(("pCallback=%p, CID=%RU32, Count=%RU32\n",
-                         pCallback, pCbCtx->uContextID, VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(pCbCtx->uContextID)));
-#endif
-    }
-
     int vrc;
     switch (pCbCtx->uFunction)
     {
         case GUEST_DISCONNECTED:
-            vrc = onGuestDisconnected(pCbCtx, pSvcCb, pCallback); /* Affects all callbacks. */
+            vrc = onGuestDisconnected(pCbCtx, pSvcCb);
             break;
 
         case GUEST_FILE_NOTIFY:
-            vrc = onFileNotify(pCbCtx, pSvcCb, pCallback);
+            vrc = onFileNotify(pCbCtx, pSvcCb);
             break;
 
@@ -291,14 +302,19 @@
     LogFlowThisFunc(("strFile=%s\n", mData.mOpenInfo.mFileName.c_str()));
 
-    /* Prepare HGCM call. */
-    VBOXHGCMSVCPARM paParms[4];
-    int i = 1; /* Context ID will be set in sendFileComannd(). */
-    paParms[i++].setUInt32(mData.mID /* Guest file ID */);
-
-    int guestRc;
-    int vrc = sendFileCommand(HOST_FILE_CLOSE, i, paParms, 30 * 1000 /* 30s timeout */,
-                              &guestRc, NULL /* ppCallback */);
-    if (pGuestRc)
-        *pGuestRc = guestRc;
+    uint32_t uContextID;
+    int vrc = generateContextID(mSession->getId(), mObjectID,
+                                &uContextID);
+    if (RT_SUCCESS(vrc))
+    {
+        /* Prepare HGCM call. */
+        VBOXHGCMSVCPARM paParms[4];
+        int i = 0;
+        paParms[i++].setUInt32(mData.mID /* Guest file ID */);
+
+        vrc = sendCommand(HOST_FILE_CLOSE, i, paParms);
+        if (RT_SUCCESS(vrc))
+            vrc = waitForStatusChange(30 * 1000 /* Timeout in ms */,
+                                      NULL /* FileStatus */);
+    }
 
     LogFlowFuncLeaveRC(vrc);
@@ -383,10 +399,8 @@
 }
 
-int GuestFile::onFileNotify(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData,
-                            GuestCtrlCallback *pCallback)
+int GuestFile::onFileNotify(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
 {
     AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
     AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
-    /* pCallback is optional. */
 
     if (pSvcCbData->mParms < 3)
@@ -401,11 +415,19 @@
     pSvcCbData->mpaParms[idx++].getUInt32(&dataCb.rc);
 
+    int guestRc = (int)dataCb.rc; /* uint32_t vs. int. */
+
     switch (dataCb.uType)
     {
         case GUEST_FILE_NOTIFYTYPE_ERROR:
-            /* No extra data. */
-            break;
+        {
+            AssertMsg(mData.mStatus != FileStatus_Error, ("File status already set to error\n"));
+
+            int rc2 = setFileStatus(FileStatus_Error, guestRc);
+            AssertRC(rc2);
+            break;
+        }
 
         case GUEST_FILE_NOTIFYTYPE_OPEN:
+        {
             if (pSvcCbData->mParms == 4)
             {
@@ -417,12 +439,23 @@
                           ("File ID %RU32 does not match context ID %RU32\n", mData.mID,
                            VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCbCtx->uContextID)));
+
+                /* Set the process status. */
+                int rc2 = setFileStatus(FileStatus_Open, guestRc);
+                if (RT_SUCCESS(vrc))
+                    vrc = rc2;
             }
             else
                 vrc = VERR_NOT_SUPPORTED;
-            break;
+
+            break;
+        }
 
         case GUEST_FILE_NOTIFYTYPE_CLOSE:
-            /* No extra data. */
-            break;
+        {
+            int rc2 = setFileStatus(FileStatus_Closed, guestRc);
+            AssertRC(rc2);
+
+            break;
+        }
 
         case GUEST_FILE_NOTIFYTYPE_READ:
@@ -431,6 +464,14 @@
                 pSvcCbData->mpaParms[idx++].getPointer(&dataCb.u.read.pvData,
                                                        &dataCb.u.read.cbData);
-
-                mData.mOffCurrent += dataCb.u.read.cbData;
+                uint32_t cbRead = dataCb.u.read.cbData;
+                if (cbRead)
+                {
+                    mData.mOffCurrent += cbRead;
+
+                    com::SafeArray<BYTE> data((size_t)cbRead);
+                    data.initFrom((BYTE*)dataCb.u.read.pvData, cbRead);
+                    fireGuestFileReadEvent(mEventSource, mSession, this, mData.mOffCurrent,
+                                           cbRead, ComSafeArrayAsInParam(data));
+                }
             }
             else
@@ -444,4 +485,8 @@
 
                 mData.mOffCurrent += dataCb.u.write.cbWritten;
+
+                if (dataCb.u.write.cbWritten)
+                    fireGuestFileWriteEvent(mEventSource, mSession, this, mData.mOffCurrent,
+                                            dataCb.u.write.cbWritten);
             }
             else
@@ -455,4 +500,8 @@
 
                 mData.mOffCurrent = dataCb.u.seek.uOffActual;
+
+                if (dataCb.u.seek.uOffActual)
+                    fireGuestFileOffsetChangedEvent(mEventSource, mSession, this,
+                                                    mData.mOffCurrent, 0 /* Processed */);
             }
             else
@@ -465,5 +514,11 @@
                 pSvcCbData->mpaParms[idx++].getUInt64(&dataCb.u.tell.uOffActual);
 
-                mData.mOffCurrent = dataCb.u.tell.uOffActual;
+                if (mData.mOffCurrent != dataCb.u.tell.uOffActual)
+                {
+                    mData.mOffCurrent = dataCb.u.tell.uOffActual;
+
+                    fireGuestFileOffsetChangedEvent(mEventSource, mSession, this,
+                                                    mData.mOffCurrent, 0 /* Processed */);
+                }
             }
             else
@@ -476,8 +531,7 @@
     }
 
-    LogFlowThisFunc(("strName=%s, uType=%RU32, rc=%Rrc, pCallback=%p\n",
-                     mData.mOpenInfo.mFileName.c_str(), dataCb.uType, dataCb.rc, pCallback));
-
-    int guestRc = (int)dataCb.rc; /* uint32_t vs. int. */
+    LogFlowThisFunc(("strName=%s, uType=%RU32, guestRc=%Rrc\n",
+                     mData.mOpenInfo.mFileName.c_str(), dataCb.uType, dataCb.rc));
+
     if (RT_SUCCESS(vrc))
     {
@@ -490,35 +544,17 @@
     }
 
-    /* Signal callback in every case (if available). */
-    if (pCallback)
-    {
-        int rc2 = pCallback->SetData(&dataCb, sizeof(dataCb));
-        if (RT_SUCCESS(vrc))
-            vrc = rc2;
-        rc2 = pCallback->Signal(guestRc);
-        if (RT_SUCCESS(vrc))
-            vrc = rc2;
-    }
-
     LogFlowFuncLeaveRC(vrc);
     return vrc;
 }
 
-int GuestFile::onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData,
-                                   GuestCtrlCallback *pCallback)
+int GuestFile::onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
 {
     AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
     AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
-    /* pCallback is optional. */
-
-    LogFlowThisFunc(("strFile=%s, pCallback=%p\n",
-                     mData.mOpenInfo.mFileName.c_str(), pCallback));
-
-    /* First, signal callback in every case. */
-    if (pCallback)
-        pCallback->Signal();
-
-    /** @todo More on onGuestDisconnected? */
-    int vrc = VINF_SUCCESS;
+
+    LogFlowThisFunc(("strFile=%s\n",
+                     mData.mOpenInfo.mFileName.c_str()));
+
+    int vrc = setFileStatus(FileStatus_Down, VINF_SUCCESS);
 
     LogFlowFuncLeaveRC(vrc);
@@ -534,5 +570,5 @@
     /* Prepare HGCM call. */
     VBOXHGCMSVCPARM paParms[8];
-    int i = 1; /* Context ID will be set in sendFileComannd(). */
+    int i = 0;
     paParms[i++].setPointer((void*)mData.mOpenInfo.mFileName.c_str(),
                             (ULONG)mData.mOpenInfo.mFileName.length() + 1);
@@ -544,6 +580,8 @@
     paParms[i++].setUInt64(mData.mOpenInfo.mInitialOffset);
 
-    int vrc = sendFileCommand(HOST_FILE_OPEN, i, paParms, 30 * 1000 /* 30s timeout */,
-                              pGuestRc, NULL /* ppCallback */);
+    int vrc = sendCommand(HOST_FILE_OPEN, i, paParms);
+    if (RT_SUCCESS(vrc))
+        vrc = waitForStatusChange(30 * 1000 /* Timeout in ms */,
+                                  NULL /* FileStatus */);
 
     LogFlowFuncLeaveRC(vrc);
@@ -551,237 +589,92 @@
 }
 
-int GuestFile::readData(uint32_t uSize, uint32_t uTimeoutMS, void *pvData, size_t cbData,
-                        size_t *pcbRead, int *pGuestRc)
+int GuestFile::readData(uint32_t uSize, uint32_t uTimeoutMS,
+                        void* pvData, uint32_t cbData, uint32_t* pcbRead)
 {
     LogFlowThisFunc(("uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
                      uSize, uTimeoutMS, pvData, cbData));
 
+    uint32_t uContextID;
+    int vrc = generateContextID(mSession->getId(), mObjectID,
+                                &uContextID);
+    if (RT_SUCCESS(vrc))
+    {
+        /* Prepare HGCM call. */
+        VBOXHGCMSVCPARM paParms[4];
+        int i = 0;
+        paParms[i++].setUInt32(mData.mID /* File handle */);
+        paParms[i++].setUInt32(uSize /* Size (in bytes) to read */);
+
+        uint32_t cbRead;
+        vrc = sendCommand(HOST_FILE_READ, i, paParms);
+        if (RT_SUCCESS(vrc))
+            vrc = waitForRead(uTimeoutMS, pvData, cbData, &cbRead);
+
+        if (RT_SUCCESS(vrc))
+        {
+            LogFlowThisFunc(("cbRead=%RU32\n", cbRead));
+
+            if (pcbRead)
+                *pcbRead = cbRead;
+        }
+    }
+
+    LogFlowFuncLeaveRC(vrc);
+    return vrc;
+}
+
+int GuestFile::readDataAt(uint64_t uOffset, uint32_t uSize, uint32_t uTimeoutMS,
+                          void* pvData, size_t cbData, size_t* pcbRead)
+{
+    LogFlowThisFunc(("uOffset=%RU64, uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
+                     uOffset, uSize, uTimeoutMS, pvData, cbData));
+
+    uint32_t uContextID;
+    int vrc = generateContextID(mSession->getId(), mObjectID,
+                                &uContextID);
+    if (RT_SUCCESS(vrc))
+    {
+
+        /* Prepare HGCM call. */
+        VBOXHGCMSVCPARM paParms[4];
+        int i = 0;
+        paParms[i++].setUInt32(mData.mID /* File handle */);
+        paParms[i++].setUInt64(uOffset /* Offset (in bytes) to start reading */);
+        paParms[i++].setUInt32(uSize /* Size (in bytes) to read */);
+
+        uint32_t cbRead;
+        int vrc = sendCommand(HOST_FILE_READ_AT, i, paParms);
+        if (RT_SUCCESS(vrc))
+            vrc = waitForRead(uTimeoutMS, pvData, cbData, &cbRead);
+
+        if (RT_SUCCESS(vrc))
+        {
+            LogFlowThisFunc(("cbRead=%RU32\n", cbRead));
+
+            if (pcbRead)
+                *pcbRead = cbRead;
+        }
+    }
+
+    LogFlowFuncLeaveRC(vrc);
+    return vrc;
+}
+
+int GuestFile::seekAt(uint64_t uOffset, GUEST_FILE_SEEKTYPE eSeekType,
+                      uint32_t uTimeoutMS, uint64_t *puOffset)
+{
+    LogFlowThisFunc(("uOffset=%RU64, uTimeoutMS=%RU32\n",
+                     uOffset, uTimeoutMS));
+
     /* Prepare HGCM call. */
     VBOXHGCMSVCPARM paParms[4];
-    int i = 1; /* Context ID will be set in sendFileComannd(). */
-    paParms[i++].setUInt32(mData.mID /* File handle */);
-    paParms[i++].setUInt32(uSize /* Size (in bytes) to read */);
-
-    GuestCtrlCallback *pCallback = NULL; int guestRc;
-    int vrc = sendFileCommand(HOST_FILE_READ, i, paParms, uTimeoutMS,
-                              &guestRc, &pCallback);
-
-    if (RT_SUCCESS(vrc))
-    {
-        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-        Assert(pCallback->GetDataSize() == sizeof(CALLBACKDATA_FILE_NOTIFY));
-        PCALLBACKDATA_FILE_NOTIFY pData = (PCALLBACKDATA_FILE_NOTIFY)pCallback->GetDataRaw();
-        AssertPtr(pData);
-        Assert(pData->uType == GUEST_FILE_NOTIFYTYPE_READ);
-
-        size_t cbRead = pData->u.read.cbData;
-        if (cbRead)
-        {
-            Assert(cbData >= cbRead);
-            memcpy(pvData, pData->u.read.pvData, cbRead);
-        }
-
-        LogFlowThisFunc(("cbRead=%RU32\n", cbRead));
-
-        if (pcbRead)
-            *pcbRead = cbRead;
-    }
-
-    callbackDelete(pCallback);
-
-    if (pGuestRc)
-        *pGuestRc = guestRc;
-
-    LogFlowFuncLeaveRC(vrc);
-    return vrc;
-}
-
-int GuestFile::readDataAt(uint64_t uOffset, uint32_t uSize, uint32_t uTimeoutMS,
-                          void *pvData, size_t cbData,
-                          size_t *pcbRead, int *pGuestRc)
-{
-    LogFlowThisFunc(("uOffset=%RU64, uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
-                     uOffset, uSize, uTimeoutMS, pvData, cbData));
-
-    /* Prepare HGCM call. */
-    VBOXHGCMSVCPARM paParms[4];
-    int i = 1; /* Context ID will be set in sendFileComannd(). */
-    paParms[i++].setUInt32(mData.mID /* File handle */);
-    paParms[i++].setUInt64(uOffset /* Offset (in bytes) to start reading */);
-    paParms[i++].setUInt32(uSize /* Size (in bytes) to read */);
-
-    GuestCtrlCallback *pCallback = NULL; int guestRc;
-    int vrc = sendFileCommand(HOST_FILE_READ_AT, i, paParms, uTimeoutMS,
-                              &guestRc, &pCallback);
-
-    if (RT_SUCCESS(vrc))
-    {
-        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-        Assert(pCallback->GetDataSize() == sizeof(CALLBACKDATA_FILE_NOTIFY));
-        PCALLBACKDATA_FILE_NOTIFY pData = (PCALLBACKDATA_FILE_NOTIFY)pCallback->GetDataRaw();
-        AssertPtr(pData);
-        Assert(pData->uType == GUEST_FILE_NOTIFYTYPE_READ);
-
-        size_t cbRead = pData->u.read.cbData;
-        if (cbRead)
-        {
-            Assert(cbData >= cbRead);
-            memcpy(pvData, pData->u.read.pvData, cbRead);
-        }
-
-        LogFlowThisFunc(("cbRead=%RU32\n", cbRead));
-
-        if (pcbRead)
-            *pcbRead = cbRead;
-    }
-
-    callbackDelete(pCallback);
-
-    if (pGuestRc)
-        *pGuestRc = guestRc;
-
-    LogFlowFuncLeaveRC(vrc);
-    return vrc;
-}
-
-int GuestFile::seekAt(uint64_t uOffset, GUEST_FILE_SEEKTYPE eSeekType,
-                      uint32_t uTimeoutMS, int *pGuestRc)
-{
-    LogFlowThisFunc(("uOffset=%RU64, uTimeoutMS=%RU32\n",
-                     uOffset, uTimeoutMS));
-
-    /* Prepare HGCM call. */
-    VBOXHGCMSVCPARM paParms[4];
-    int i = 1; /* Context ID will be set in sendFileComannd(). */
+    int i = 0;
     paParms[i++].setUInt32(mData.mID /* File handle */);
     paParms[i++].setUInt32(eSeekType /* Seek method */);
     paParms[i++].setUInt64(uOffset /* Offset (in bytes) to start reading */);
 
-    int guestRc;
-    int vrc = sendFileCommand(HOST_FILE_SEEK, i, paParms, uTimeoutMS,
-                              &guestRc, NULL /* ppCallback */);
-    if (pGuestRc)
-        *pGuestRc = guestRc;
-
-    LogFlowFuncLeaveRC(vrc);
-    return vrc;
-}
-
-/**
- * Handles the common parts of sending a file command to the guest.
- * If ppCallback is returned it must be removed via callbackRemove()
- * by the caller in any case.
- *
- * @return  IPRT status code.
- * @param   uFunction               HGCM function of command to send.
- * @param   uParms                  Number of HGCM parameters to send.
- *                                  At least one parameter must be present.
- * @param   paParms                 Array of HGCM parameters to send.
- *                                  Index [0] must not be used and will be
- *                                  filled out by the function.
- * @param   uTimeoutMS              Timeout (in ms).
- * @param   pGuestRc                Guest result. Optional.
- * @param   ppCallback              Pointer which will receive the callback for
- *                                  further processing by the caller. Must
- *                                  be deleted with callbackDelete() when done. Optional.
- */
-int GuestFile::sendFileCommand(uint32_t uFunction, uint32_t uParms, PVBOXHGCMSVCPARM paParms,
-                               uint32_t uTimeoutMS, int *pGuestRc, GuestCtrlCallback **ppCallback)
-{
-    AssertReturn(uParms, VERR_INVALID_PARAMETER);
-    AssertPtrReturn(paParms, VERR_INVALID_POINTER);
-    /** pGuestRc is optional. */
-    /** ppCallback is optional. */
-
-    LogFlowThisFunc(("strFile=%s, uFunction=%RU32, uParms=%RU32\n",
-                     mData.mOpenInfo.mFileName.c_str(), uFunction, uParms));
-
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    AssertPtr(mData.mSession);
-    uint32_t uProtocol = mData.mSession->getProtocolVersion();
-    if (uProtocol < 2)
-        return VERR_NOT_SUPPORTED;
-
-    int vrc = VINF_SUCCESS;
-    uint32_t uContextID = 0;
-
-    GuestCtrlCallback *pCallback;
-    try
-    {
-        pCallback = new GuestCtrlCallback();
-    }
-    catch(std::bad_alloc &)
-    {
-        vrc = VERR_NO_MEMORY;
-    }
-
+    int vrc = sendCommand(HOST_FILE_SEEK, i, paParms);
     if (RT_SUCCESS(vrc))
-    {
-        /* Create callback and add it to the map. */
-        vrc = pCallback->Init(CALLBACKTYPE_FILE_NOTIFY);
-        if (RT_SUCCESS(vrc))
-            vrc = callbackAdd(pCallback, &uContextID);
-    }
-
-    if (RT_SUCCESS(vrc))
-    {
-        /* Assign context ID. */
-        paParms[0].setUInt32(uContextID);
-
-        GuestSession *pSession = mData.mSession;
-        AssertPtr(pSession);
-
-        alock.release(); /* Drop the write lock again. */
-
-        /* Note: Don't hold the write lock in here. */
-        vrc = sendCommand(uFunction, uParms, paParms);
-
-        if (RT_SUCCESS(vrc))
-        {
-            /*
-             * Let's wait for the process being started.
-             * Note: Be sure not keeping a AutoRead/WriteLock here.
-             */
-            LogFlowThisFunc(("Waiting for callback (%RU32ms) ...\n",
-                             uTimeoutMS));
-            vrc = pCallback->Wait(uTimeoutMS);
-            if (RT_SUCCESS(vrc)) /* Wait was successful, check for supplied information. */
-            {
-                int guestRc = pCallback->GetResultCode();
-                if (RT_SUCCESS(guestRc))
-                {
-                    /* Nothing to do here yet. */
-                }
-                else
-                    vrc = VERR_GSTCTL_GUEST_ERROR;
-
-                if (pGuestRc)
-                    *pGuestRc = guestRc;
-                LogFlowThisFunc(("Callback returned rc=%Rrc\n", guestRc));
-            }
-        }
-
-        alock.acquire(); /* Get write lock again. */
-
-        AssertPtr(pCallback);
-        int rc2 = callbackRemove(uContextID);
-        if (RT_SUCCESS(vrc))
-            vrc = rc2;
-
-        if (ppCallback)
-        {
-            /* Return callback to the caller which then will be
-             * responsible for removing it. Don't forget to lock write
-             * access before using this callback then! */
-            *ppCallback = pCallback;
-        }
-        else
-        {
-            delete pCallback;
-        }
-    }
+        vrc = waitForOffsetChange(uTimeoutMS, puOffset);
 
     LogFlowFuncLeaveRC(vrc);
@@ -798,6 +691,144 @@
 }
 
-int GuestFile::writeData(uint32_t uTimeoutMS, void *pvData, size_t cbData,
-                         uint32_t *pcbWritten, int *pGuestRc)
+/* Does not do locking; caller is responsible for that! */
+int GuestFile::setFileStatus(FileStatus_T fileStatus, int fileRc)
+{
+    LogFlowThisFunc(("oldStatus=%ld, newStatus=%ld, fileRc=%Rrc\n",
+                     mData.mStatus, fileStatus, fileRc));
+
+#ifdef VBOX_STRICT
+    if (fileStatus == FileStatus_Error)
+    {
+        AssertMsg(RT_FAILURE(fileRc), ("Guest rc must be an error (%Rrc)\n", fileRc));
+    }
+    else
+        AssertMsg(RT_SUCCESS(fileRc), ("Guest rc must not be an error (%Rrc)\n", fileRc));
+#endif
+
+    if (mData.mStatus != fileStatus)
+    {
+        mData.mStatus = fileStatus;
+
+        ComObjPtr<GuestErrorInfo> errorInfo;
+        HRESULT hr = errorInfo.createObject();
+        ComAssertComRC(hr);
+        if (RT_FAILURE(fileRc))
+        {
+            int rc2 = errorInfo->init(fileRc, guestErrorToString(fileRc));
+            AssertRC(rc2);
+        }
+
+        fireGuestFileStateChangedEvent(mEventSource, mSession,
+                                       this, mData.mStatus, errorInfo);
+    }
+
+    return VINF_SUCCESS;
+}
+
+int GuestFile::waitForOffsetChange(uint32_t uTimeoutMS, uint64_t *puOffset)
+{
+    return VINF_SUCCESS;
+}
+
+int GuestFile::waitForRead(uint32_t uTimeoutMS, void* pvData, size_t cbData, uint32_t *pcbRead)
+{
+    return VINF_SUCCESS;
+}
+
+int GuestFile::waitForStatusChange(uint32_t uTimeoutMS, FileStatus_T *pFileStatus)
+{
+    int vrc;
+
+    /** @todo Parameter validation. */
+
+    ComPtr<IEventListener> pListener;
+    HRESULT hr = mEventSource->CreateListener(pListener.asOutParam());
+    if (SUCCEEDED(hr))
+    {
+        com::SafeArray <VBoxEventType_T> eventTypes(1);
+        eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
+        hr = mEventSource->RegisterListener(pListener, ComSafeArrayAsInParam(eventTypes), false);
+    }
+    else
+        vrc = VERR_COM_UNEXPECTED;
+
+    if (SUCCEEDED(hr))
+    {
+        LogFlowThisFunc(("Waiting for guest file status change event (timeout=%RU32ms) ...\n",
+                         uTimeoutMS));
+
+        vrc = VINF_SUCCESS;
+
+        uint64_t u64Started = RTTimeMilliTS();
+        bool fSignalled = false;
+        do
+        {
+            unsigned cMsWait;
+            if (uTimeoutMS == RT_INDEFINITE_WAIT)
+                cMsWait = 1000;
+            else
+            {
+                uint64_t cMsElapsed = RTTimeMilliTS() - u64Started;
+                if (cMsElapsed >= uTimeoutMS)
+                    break; /* timed out */
+                cMsWait = RT_MIN(1000, uTimeoutMS - (uint32_t)cMsElapsed);
+            }
+
+            ComPtr<IEvent> pEvent;
+            hr = mEventSource->GetEvent(pListener, cMsWait, pEvent.asOutParam());
+            if (   SUCCEEDED(hr)
+                && !pEvent.isNull())
+            {
+                VBoxEventType_T aType;
+                hr = pEvent->COMGETTER(Type)(&aType);
+                ComAssertComRC(hr);
+                switch (aType)
+                {
+                    case VBoxEventType_OnGuestFileStateChanged:
+                    {
+                        ComPtr<IGuestFileStateChangedEvent> pChangedEvent = pEvent;
+                        Assert(!pChangedEvent.isNull());
+
+                        ComPtr<IGuestFile> pFile;
+                        pChangedEvent->COMGETTER(File)(pFile.asOutParam());
+                        Assert(!pFile.isNull());
+
+                        if (pFile != this)
+                            continue;
+                    }
+
+                    default:
+                        AssertMsgFailed(("Unhandled event type %ld\n", aType));
+                        break;
+                }
+
+                fSignalled = true;
+                break;
+            }
+
+        } while (!fSignalled);
+
+        if (   RT_SUCCESS(vrc)
+            && !fSignalled)
+        {
+            vrc = VERR_TIMEOUT;
+        }
+
+        mEventSource->UnregisterListener(pListener);
+    }
+    else
+        vrc = VERR_COM_UNEXPECTED;
+
+    LogFlowFuncLeaveRC(vrc);
+    return vrc;
+}
+
+int GuestFile::waitForWrite(uint32_t uTimeoutMS, uint32_t *pcbWritten)
+{
+    return VINF_SUCCESS;
+}
+
+int GuestFile::writeData(uint32_t uTimeoutMS, void *pvData, uint32_t cbData,
+                         uint32_t *pcbWritten)
 {
     AssertPtrReturn(pvData, VERR_INVALID_POINTER);
@@ -807,35 +838,30 @@
                      uTimeoutMS, pvData, cbData));
 
-    /* Prepare HGCM call. */
-    VBOXHGCMSVCPARM paParms[4];
-    int i = 1; /* Context ID will be set in sendFileComannd(). */
-    paParms[i++].setUInt32(mData.mID /* File handle */);
-    paParms[i++].setUInt32(cbData /* Size (in bytes) to write */);
-    paParms[i++].setPointer(pvData, cbData);
-
-    GuestCtrlCallback *pCallback = NULL; int guestRc;
-    int vrc = sendFileCommand(HOST_FILE_WRITE, i, paParms, uTimeoutMS,
-                              &guestRc, &pCallback);
-
+    uint32_t uContextID;
+    int vrc = generateContextID(mSession->getId(), mObjectID,
+                                &uContextID);
     if (RT_SUCCESS(vrc))
     {
-        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-        Assert(pCallback->GetDataSize() == sizeof(CALLBACKDATA_FILE_NOTIFY));
-        PCALLBACKDATA_FILE_NOTIFY pData = (PCALLBACKDATA_FILE_NOTIFY)pCallback->GetDataRaw();
-        AssertPtr(pData);
-        Assert(pData->uType == GUEST_FILE_NOTIFYTYPE_WRITE);
-
-        size_t cbWritten = pData->u.write.cbWritten;
-        LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten));
-
-        if (pcbWritten)
-            *pcbWritten = cbWritten;
-    }
-
-    callbackDelete(pCallback);
-
-    if (pGuestRc)
-        *pGuestRc = guestRc;
+        /* Prepare HGCM call. */
+        VBOXHGCMSVCPARM paParms[8];
+        int i = 0;
+        paParms[i++].setUInt32(uContextID);
+        paParms[i++].setUInt32(mData.mID /* File handle */);
+        paParms[i++].setUInt32(cbData /* Size (in bytes) to write */);
+        paParms[i++].setPointer(pvData, cbData);
+
+        uint32_t cbWritten;
+        vrc = sendCommand(HOST_FILE_WRITE, i, paParms);
+        if (RT_SUCCESS(vrc))
+            vrc = waitForWrite(uTimeoutMS, &cbWritten);
+
+        if (RT_SUCCESS(vrc))
+        {
+            LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten));
+
+            if (cbWritten)
+                *pcbWritten = cbWritten;
+        }
+    }
 
     LogFlowFuncLeaveRC(vrc);
@@ -844,6 +870,5 @@
 
 int GuestFile::writeDataAt(uint64_t uOffset, uint32_t uTimeoutMS,
-                           void *pvData, size_t cbData,
-                           uint32_t *pcbWritten, int *pGuestRc)
+                           void *pvData, uint32_t cbData, uint32_t *pcbWritten)
 {
     AssertPtrReturn(pvData, VERR_INVALID_POINTER);
@@ -853,36 +878,34 @@
                      uOffset, uTimeoutMS, pvData, cbData));
 
-    /* Prepare HGCM call. */
-    VBOXHGCMSVCPARM paParms[4];
-    int i = 1; /* Context ID will be set in sendFileComannd(). */
-    paParms[i++].setUInt32(mData.mID /* File handle */);
-    paParms[i++].setUInt64(uOffset /* Offset where to starting writing */);
-    paParms[i++].setUInt32(cbData /* Size (in bytes) to write */);
-    paParms[i++].setPointer(pvData, cbData);
-
-    GuestCtrlCallback *pCallback = NULL; int guestRc;
-    int vrc = sendFileCommand(HOST_FILE_WRITE_AT, i, paParms, uTimeoutMS,
-                              &guestRc, &pCallback);
-
+    uint32_t uContextID;
+    int vrc = generateContextID(mSession->getId(), mObjectID,
+                                &uContextID);
     if (RT_SUCCESS(vrc))
     {
-        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-        Assert(pCallback->GetDataSize() == sizeof(CALLBACKDATA_FILE_NOTIFY));
-        PCALLBACKDATA_FILE_NOTIFY pData = (PCALLBACKDATA_FILE_NOTIFY)pCallback->GetDataRaw();
-        AssertPtr(pData);
-        Assert(pData->uType == GUEST_FILE_NOTIFYTYPE_WRITE);
-
-        size_t cbWritten = pData->u.write.cbWritten;
-        LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten));
-
-        if (pcbWritten)
-            *pcbWritten = cbWritten;
-    }
-
-    callbackDelete(pCallback);
-
-    if (pGuestRc)
-        *pGuestRc = guestRc;
+        /* Prepare HGCM call. */
+        VBOXHGCMSVCPARM paParms[8];
+        int i = 0;
+        paParms[i++].setUInt32(mData.mID /* File handle */);
+        paParms[i++].setUInt64(uOffset /* Offset where to starting writing */);
+        paParms[i++].setUInt32(cbData /* Size (in bytes) to write */);
+        paParms[i++].setPointer(pvData, cbData);
+
+        vrc = sendCommand(HOST_FILE_WRITE_AT, i, paParms);
+        if (RT_SUCCESS(vrc))
+        {
+            uint32_t cbWritten;
+            vrc = sendCommand(HOST_FILE_WRITE, i, paParms);
+            if (RT_SUCCESS(vrc))
+                vrc = waitForWrite(uTimeoutMS, &cbWritten);
+
+            if (RT_SUCCESS(vrc))
+            {
+                LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten));
+
+                if (cbWritten)
+                    *pcbWritten = cbWritten;
+            }
+        }
+    }
 
     LogFlowFuncLeaveRC(vrc);
@@ -909,6 +932,6 @@
      * work first and then return an error. */
 
-    AssertPtr(mData.mSession);
-    int rc2 = mData.mSession->fileRemoveFromList(this);
+    AssertPtr(mSession);
+    int rc2 = mSession->fileRemoveFromList(this);
     if (RT_SUCCESS(rc))
         rc = rc2;
@@ -964,7 +987,7 @@
     HRESULT hr = S_OK;
 
-    size_t cbRead; int guestRc;
+    uint32_t cbRead;
     int vrc = readData(aToRead, aTimeoutMS,
-                       data.raw(), aToRead, &cbRead, &guestRc);
+                       data.raw(), aToRead, &cbRead);
     if (RT_SUCCESS(vrc))
     {
@@ -977,8 +1000,4 @@
         switch (vrc)
         {
-            case VERR_GSTCTL_GUEST_ERROR:
-                hr = GuestFile::setErrorExternal(this, guestRc);
-                break;
-
             default:
                 hr = setError(VBOX_E_IPRT_ERROR,
@@ -1013,7 +1032,7 @@
     HRESULT hr = S_OK;
 
-    size_t cbRead; int guestRc;
+    size_t cbRead;
     int vrc = readDataAt(aOffset, aToRead, aTimeoutMS,
-                         data.raw(), aToRead, &cbRead, &guestRc);
+                         data.raw(), aToRead, &cbRead);
     if (RT_SUCCESS(vrc))
     {
@@ -1026,8 +1045,4 @@
         switch (vrc)
         {
-            case VERR_GSTCTL_GUEST_ERROR:
-                hr = GuestFile::setErrorExternal(this, guestRc);
-                break;
-
             default:
                 hr = setError(VBOX_E_IPRT_ERROR,
@@ -1073,15 +1088,10 @@
     }
 
-    int guestRc;
     int vrc = seekAt(aOffset, eSeekType,
-                     30 * 1000 /* 30s timeout */, &guestRc);
+                     30 * 1000 /* 30s timeout */, NULL /* puOffset */);
     if (RT_FAILURE(vrc))
     {
         switch (vrc)
         {
-            case VERR_GSTCTL_GUEST_ERROR:
-                hr = GuestFile::setErrorExternal(this, guestRc);
-                break;
-
             default:
                 hr = setError(VBOX_E_IPRT_ERROR,
@@ -1123,14 +1133,11 @@
     HRESULT hr = S_OK;
 
-    com::SafeArray<BYTE> data(ComSafeArrayInArg(aData)); int guestRc;
-    int vrc = writeData(aTimeoutMS, data.raw(), data.size(), (uint32_t*)aWritten, &guestRc);
+    com::SafeArray<BYTE> data(ComSafeArrayInArg(aData));
+    int vrc = writeData(aTimeoutMS, data.raw(), (uint32_t)data.size(),
+                        (uint32_t*)aWritten);
     if (RT_FAILURE(vrc))
     {
         switch (vrc)
         {
-            case VERR_GSTCTL_GUEST_ERROR:
-                hr = GuestFile::setErrorExternal(this, guestRc);
-                break;
-
             default:
                 hr = setError(VBOX_E_IPRT_ERROR,
@@ -1162,14 +1169,11 @@
     HRESULT hr = S_OK;
 
-    com::SafeArray<BYTE> data(ComSafeArrayInArg(aData)); int guestRc;
-    int vrc = writeData(aTimeoutMS, data.raw(), data.size(), (uint32_t*)aWritten, &guestRc);
+    com::SafeArray<BYTE> data(ComSafeArrayInArg(aData));
+    int vrc = writeData(aTimeoutMS, data.raw(), (uint32_t)data.size(),
+                         (uint32_t*)aWritten);
     if (RT_FAILURE(vrc))
     {
         switch (vrc)
         {
-            case VERR_GSTCTL_GUEST_ERROR:
-                hr = GuestFile::setErrorExternal(this, guestRc);
-                break;
-
             default:
                 hr = setError(VBOX_E_IPRT_ERROR,
Index: /trunk/src/VBox/Main/src-client/GuestImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/GuestImpl.cpp	(revision 45414)
+++ /trunk/src/VBox/Main/src-client/GuestImpl.cpp	(revision 45415)
@@ -80,18 +80,18 @@
 
     ULONG aMemoryBalloonSize;
-    HRESULT ret = mParent->machine()->COMGETTER(MemoryBalloonSize)(&aMemoryBalloonSize);
-    if (ret == S_OK)
+    HRESULT hr = mParent->machine()->COMGETTER(MemoryBalloonSize)(&aMemoryBalloonSize);
+    if (hr == S_OK) /** @todo r=andy SUCCEEDED? */
         mMemoryBalloonSize = aMemoryBalloonSize;
     else
-        mMemoryBalloonSize = 0;                     /* Default is no ballooning */
+        mMemoryBalloonSize = 0; /* Default is no ballooning */
 
     BOOL fPageFusionEnabled;
-    ret = mParent->machine()->COMGETTER(PageFusionEnabled)(&fPageFusionEnabled);
-    if (ret == S_OK)
+    hr = mParent->machine()->COMGETTER(PageFusionEnabled)(&fPageFusionEnabled);
+    if (hr == S_OK) /** @todo r=andy SUCCEEDED? */
         mfPageFusionEnabled = fPageFusionEnabled;
     else
-        mfPageFusionEnabled = false;                /* Default is no page fusion*/
-
-    mStatUpdateInterval = 0;                    /* Default is not to report guest statistics at all */
+        mfPageFusionEnabled = false; /* Default is no page fusion*/
+
+    mStatUpdateInterval = 0; /* Default is not to report guest statistics at all */
     mCollectVMMStats = false;
 
@@ -106,5 +106,13 @@
     int vrc = RTTimerLRCreate(&mStatTimer, 1000 /* ms */,
                               &Guest::staticUpdateStats, this);
-    AssertMsgRC(vrc, ("Failed to create guest statistics update timer(%Rra)\n", vrc));
+    AssertMsgRC(vrc, ("Failed to create guest statistics update timer (%Rrc)\n", vrc));
+
+#ifdef VBOX_WITH_GUEST_CONTROL
+    unconst(mEventSource).createObject();
+    Assert(!mEventSource.isNull());
+    hr = mEventSource->init(static_cast<IGuest*>(this));
+#else
+    hr = S_OK;
+#endif
 
     try
@@ -117,8 +125,8 @@
     catch(std::bad_alloc &)
     {
-        return E_OUTOFMEMORY;
-    }
-
-    return S_OK;
+        hr = E_OUTOFMEMORY;
+    }
+
+    return hr;
 }
 
@@ -167,4 +175,7 @@
 #endif
 
+#ifdef VBOX_WITH_GUEST_CONTROL
+    unconst(mEventSource).setNull();
+#endif
     unconst(mParent) = NULL;
 
Index: /trunk/src/VBox/Main/src-client/GuestProcessImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/GuestProcessImpl.cpp	(revision 45414)
+++ /trunk/src/VBox/Main/src-client/GuestProcessImpl.cpp	(revision 45415)
@@ -29,8 +29,10 @@
 *   Header Files                                                               *
 *******************************************************************************/
+#include "GuestErrorInfoImpl.h"
 #include "GuestProcessImpl.h"
 #include "GuestSessionImpl.h"
 #include "GuestCtrlImplPrivate.h"
 #include "ConsoleImpl.h"
+#include "VBoxEvents.h"
 
 #include "Global.h"
@@ -40,5 +42,7 @@
 
 #include <iprt/asm.h>
+#include <iprt/cpp/utils.h> /* For unconst(). */
 #include <iprt/getopt.h>
+
 #include <VBox/com/array.h>
 
@@ -92,9 +96,5 @@
     mData.mStatus = ProcessStatus_Undefined;
 
-    mData.mWaitCount = 0;
-    mData.mWaitEvent = NULL;
-
-    HRESULT hr = BaseFinalConstruct();
-    return hr;
+    return BaseFinalConstruct();
 }
 
@@ -124,4 +124,13 @@
 
     int vrc = bindToSession(aConsole, aSession, aProcessID /* Object ID */);
+    if (RT_SUCCESS(vrc))
+    {
+        unconst(mEventSource).createObject();
+        Assert(!mEventSource.isNull());
+        HRESULT hr = mEventSource->init(static_cast<IGuestProcess*>(this));
+        if (FAILED(hr))
+            vrc = VERR_COM_UNEXPECTED;
+    }
+
     if (RT_SUCCESS(vrc))
     {
@@ -156,18 +165,5 @@
 
 #ifdef VBOX_WITH_GUEST_CONTROL
-    /*
-     * Cancel + remove all callbacks + waiters.
-     * Note: Deleting them is the job of the caller!
-     */
-    callbackRemoveAll();
-
-    if (mData.mWaitEvent)
-    {
-        int rc2 = mData.mWaitEvent->Cancel();
-        if (RT_SUCCESS(vrc))
-            vrc = rc2;
-    }
-
-    mData.mStatus = ProcessStatus_Down; /** @todo Correct? */
+    unconst(mEventSource).setNull();
 #endif
 
@@ -346,20 +342,4 @@
     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
-    /* Get the optional callback associated to this context ID.
-     * The callback may not be around anymore if just kept locally by the caller when
-     * doing the actual HGCM sending stuff. */
-    GuestCtrlCallback *pCallback = NULL;
-    GuestCtrlCallbacks::const_iterator it
-        = mObject.mCallbacks.find(VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(pCbCtx->uContextID));
-    if (it != mObject.mCallbacks.end())
-    {
-        pCallback = it->second;
-        AssertPtr(pCallback);
-#ifdef DEBUG
-        LogFlowThisFunc(("pCallback=%p, CID=%RU32, Count=%RU32\n",
-                         pCallback, pCbCtx->uContextID, VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(pCbCtx->uContextID)));
-#endif
-    }
-
     int vrc;
     switch (pCbCtx->uFunction)
@@ -367,5 +347,5 @@
         case GUEST_DISCONNECTED:
         {
-            vrc = onGuestDisconnected(pCbCtx, pCallback, pSvcCb); /* Affects all callbacks. */
+            vrc = onGuestDisconnected(pCbCtx, pSvcCb);
             break;
         }
@@ -373,5 +353,5 @@
         case GUEST_EXEC_STATUS:
         {
-            vrc = onProcessStatusChange(pCbCtx, pCallback, pSvcCb);
+            vrc = onProcessStatusChange(pCbCtx, pSvcCb);
             break;
         }
@@ -379,5 +359,5 @@
         case GUEST_EXEC_OUTPUT:
         {
-            vrc = onProcessOutput(pCbCtx, pCallback, pSvcCb);
+            vrc = onProcessOutput(pCbCtx, pSvcCb);
             break;
         }
@@ -385,5 +365,5 @@
         case GUEST_EXEC_INPUT_STATUS:
         {
-            vrc = onProcessInputStatus(pCbCtx, pCallback, pSvcCb);
+            vrc = onProcessInputStatus(pCbCtx, pSvcCb);
             break;
         }
@@ -422,5 +402,5 @@
 
          */
-        if (mObject.mSession->getProtocolVersion() < 2)
+        if (mSession->getProtocolVersion() < 2)
         {
             /* Simply ignore the stale requests. */
@@ -487,5 +467,5 @@
 
         case VERR_MAX_PROCS_REACHED:
-            strError += Utf8StrFmt(tr("Maximum number of parallel guest processes has been reached"));
+            strError += Utf8StrFmt(tr("Maximum number of concurrent guest processes has been reached"));
             break;
 
@@ -526,27 +506,12 @@
 }
 
-int GuestProcess::onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
-                                      GuestCtrlCallback *pCallback, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
-{
-    AssertPtrReturn(pCallback, VERR_INVALID_POINTER);
-
-    LogFlowThisFunc(("uPID=%RU32, pCallback=%p\n", mData.mPID, pCallback));
-
-    mData.mStatus = ProcessStatus_Down;
-
-    /* First, signal callback in every case. */
-    if (pCallback)
-        pCallback->Signal();
-
-    /* Do we need to report a termination? */
-    ProcessWaitResult_T waitRes;
-    if (mData.mProcess.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
-        waitRes = ProcessWaitResult_Status; /* No, just report a status. */
-    else
-        waitRes = ProcessWaitResult_Terminate;
-
-    /* Signal in any case. */
-    int vrc = signalWaiters(waitRes);
-    AssertRC(vrc);
+int GuestProcess::onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
+{
+    AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
+    AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
+
+    LogFlowThisFunc(("uPID=%RU32\n", mData.mPID));
+
+    int vrc = setProcessStatus(ProcessStatus_Down, VINF_SUCCESS);
 
     LogFlowFuncLeaveRC(vrc);
@@ -554,9 +519,9 @@
 }
 
-int GuestProcess::onProcessInputStatus(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
-                                       GuestCtrlCallback *pCallback, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
-{
-    AssertPtrReturn(pCallback, VERR_INVALID_POINTER);
+int GuestProcess::onProcessInputStatus(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
+{
+    AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
     AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
+    /* pCallback is optional. */
 
     if (pSvcCbData->mParms < 5)
@@ -574,28 +539,36 @@
     AssertRCReturn(vrc, vrc);
 
-    LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RI32, cbProcessed=%RU32, pCallback=%p\n",
-                     dataCb.uPID, dataCb.uStatus, dataCb.uFlags, dataCb.uProcessed, pCallback));
+    LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RI32, cbProcessed=%RU32\n",
+                     dataCb.uPID, dataCb.uStatus, dataCb.uFlags, dataCb.uProcessed));
 
     vrc = checkPID(dataCb.uPID);
     AssertRCReturn(vrc, vrc);
 
-    /* First, signal callback in every case (if available). */
-    if (pCallback)
-    {
-        vrc = pCallback->SetData(&dataCb, sizeof(dataCb));
-
-        int rc2 = pCallback->Signal();
-        if (RT_SUCCESS(vrc))
-            vrc = rc2;
-    }
-
-    /* Then do the WaitFor signalling stuff. */
-    uint32_t uWaitFlags = mData.mWaitEvent
-                        ? mData.mWaitEvent->GetWaitFlags() : 0;
-    if (uWaitFlags & ProcessWaitForFlag_StdIn)
-    {
-        int rc2 = signalWaiters(ProcessWaitResult_StdIn);
-        if (RT_SUCCESS(vrc))
-            vrc = rc2;
+    ProcessInputStatus_T inputStatus = ProcessInputStatus_Undefined;
+    switch (dataCb.uStatus)
+    {
+        case INPUT_STS_WRITTEN:
+            inputStatus = ProcessInputStatus_Written;
+            break;
+        case INPUT_STS_ERROR:
+            inputStatus = ProcessInputStatus_Broken;
+            break;
+        case INPUT_STS_TERMINATED:
+            inputStatus = ProcessInputStatus_Broken;
+            break;
+        case INPUT_STS_OVERFLOW:
+            inputStatus = ProcessInputStatus_Overflow;
+            break;
+        case INPUT_STS_UNDEFINED:
+            /* Fall through is intentional. */
+        default:
+            AssertMsg(!dataCb.uProcessed, ("Processed data is not 0 in undefined input state\n"));
+            break;
+    }
+
+    if (inputStatus != ProcessInputStatus_Undefined)
+    {
+        fireGuestProcessInputNotifyEvent(mEventSource, mSession, this,
+                                         mData.mPID, inputStatus, 0 /* StdIn */, dataCb.uProcessed);
     }
 
@@ -604,14 +577,13 @@
 }
 
-int GuestProcess::onProcessNotifyIO(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
-                                    GuestCtrlCallback *pCallback, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
-{
-    AssertPtrReturn(pCallback, VERR_INVALID_POINTER);
+int GuestProcess::onProcessNotifyIO(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
+{
+    AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
+    AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
 
     return VERR_NOT_IMPLEMENTED;
 }
 
-int GuestProcess::onProcessStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
-                                        GuestCtrlCallback *pCallback, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
+int GuestProcess::onProcessStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
 {
     /* pCallback is optional. */
@@ -632,6 +604,6 @@
     AssertRCReturn(vrc, vrc);
 
-    LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RU32, pCallback=%p, pSvcCbData=%p\n",
-                     dataCb.uPID, dataCb.uStatus, dataCb.uFlags, pCallback, pSvcCbData));
+    LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RU32\n",
+                     dataCb.uPID, dataCb.uStatus, dataCb.uFlags));
 
     vrc = checkPID(dataCb.uPID);
@@ -641,20 +613,8 @@
     int procRc = VINF_SUCCESS;
 
-    bool fSignalWaiters = false;
-    ProcessWaitResult_T waitRes;
-
-    uint32_t uWaitFlags = mData.mWaitEvent
-                        ? mData.mWaitEvent->GetWaitFlags() : 0;
     switch (dataCb.uStatus)
     {
         case PROC_STS_STARTED:
         {
-            fSignalWaiters = (uWaitFlags & ProcessWaitForFlag_Start);
-            /* If the caller only wants to wait until the process has been started,
-             * notify in any case. */
-            if (mData.mProcess.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
-                fSignalWaiters = true;
-            waitRes = ProcessWaitResult_Start;
-
             procStatus = ProcessStatus_Started;
             mData.mPID = dataCb.uPID; /* Set the process PID. */
@@ -664,7 +624,4 @@
         case PROC_STS_TEN:
         {
-            fSignalWaiters = true; /* Signal in any case. */
-            waitRes = ProcessWaitResult_Terminate;
-
             procStatus = ProcessStatus_TerminatedNormally;
             mData.mExitCode = dataCb.uFlags; /* Contains the exit code. */
@@ -674,7 +631,4 @@
         case PROC_STS_TES:
         {
-            fSignalWaiters = true; /* Signal in any case. */
-            waitRes = ProcessWaitResult_Terminate;
-
             procStatus = ProcessStatus_TerminatedSignal;
             mData.mExitCode = dataCb.uFlags; /* Contains the signal. */
@@ -684,7 +638,4 @@
         case PROC_STS_TEA:
         {
-            fSignalWaiters = true; /* Signal in any case. */
-            waitRes = ProcessWaitResult_Terminate;
-
             procStatus = ProcessStatus_TerminatedAbnormally;
             break;
@@ -693,7 +644,4 @@
         case PROC_STS_TOK:
         {
-            fSignalWaiters = true; /* Signal in any case. */
-            waitRes = ProcessWaitResult_Timeout;
-
             procStatus = ProcessStatus_TimedOutKilled;
             break;
@@ -702,7 +650,4 @@
         case PROC_STS_TOA:
         {
-            fSignalWaiters = true; /* Signal in any case. */
-            waitRes = ProcessWaitResult_Timeout;
-
             procStatus = ProcessStatus_TimedOutAbnormally;
             break;
@@ -711,11 +656,4 @@
         case PROC_STS_DWN:
         {
-            fSignalWaiters = true; /* Signal in any case. */
-            /* Do we need to report termination? */
-            if (mData.mProcess.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
-                waitRes = ProcessWaitResult_Status;
-            else
-                waitRes = ProcessWaitResult_Terminate;
-
             procStatus = ProcessStatus_Down;
             break;
@@ -724,7 +662,4 @@
         case PROC_STS_ERROR:
         {
-            fSignalWaiters = true; /* Signal in any case. */
-            waitRes = ProcessWaitResult_Error;
-
             procRc = dataCb.uFlags; /* mFlags contains the IPRT error sent from the guest. */
             procStatus = ProcessStatus_Error;
@@ -736,7 +671,4 @@
         {
             /* Silently skip this request. */
-            fSignalWaiters = true; /* Signal in any case. */
-            waitRes = ProcessWaitResult_Status;
-
             procStatus = ProcessStatus_Undefined;
             break;
@@ -744,6 +676,6 @@
     }
 
-    LogFlowThisFunc(("Got rc=%Rrc, waitRes=%d, procSts=%ld, procRc=%Rrc, fSignalWaiters=%RTbool\n",
-                     vrc, waitRes, procStatus, procRc, fSignalWaiters));
+    LogFlowThisFunc(("Got rc=%Rrc, procSts=%ld, procRc=%Rrc\n",
+                     vrc, procStatus, procRc));
 
     /* Set the process status. */
@@ -752,29 +684,10 @@
         vrc = rc2;
 
-    /*
-     * Now do the signalling stuff.
-     */
-    if (pCallback)
-    {
-        rc2 = pCallback->Signal(procRc);
-        if (RT_SUCCESS(vrc))
-            vrc = rc2;
-    }
-
-    if (fSignalWaiters)
-    {
-        rc2 = signalWaiters(waitRes, procRc);
-        if (RT_SUCCESS(vrc))
-            vrc = rc2;
-    }
-
     LogFlowFuncLeaveRC(vrc);
     return vrc;
 }
 
-int GuestProcess::onProcessOutput(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
-                                  GuestCtrlCallback *pCallback, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
-{
-    AssertPtrReturn(pCallback, VERR_INVALID_POINTER);
+int GuestProcess::onProcessOutput(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
+{
     AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
 
@@ -793,51 +706,15 @@
     AssertRCReturn(vrc, vrc);
 
-    LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RI32, pvData=%p, cbData=%RU32, pCallback=%p\n",
-                     dataCb.uPID, dataCb.uHandle, dataCb.uFlags, dataCb.pvData, dataCb.cbData, pCallback));
+    LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RI32, pvData=%p, cbData=%RU32\n",
+                     dataCb.uPID, dataCb.uHandle, dataCb.uFlags, dataCb.pvData, dataCb.cbData));
 
     vrc = checkPID(dataCb.uPID);
     AssertRCReturn(vrc, vrc);
 
-    /* First, signal callback in every case (if available). */
-    if (pCallback)
-    {
-        vrc = pCallback->SetData(&dataCb, sizeof(dataCb));
-
-        int rc2 = pCallback->Signal();
-        if (RT_SUCCESS(vrc))
-            vrc = rc2;
-    }
-
-    /* Then do the WaitFor signalling stuff. */
-    BOOL fSignal = FALSE;
-    uint32_t uWaitFlags = mData.mWaitEvent
-                        ? mData.mWaitEvent->GetWaitFlags() : 0;
-
-    if (    (uWaitFlags & ProcessWaitForFlag_StdOut)
-         || (uWaitFlags & ProcessWaitForFlag_StdErr))
-    {
-        fSignal = TRUE;
-    }
-    else if (   (uWaitFlags & ProcessWaitForFlag_StdOut)
-             && (dataCb.uHandle == OUTPUT_HANDLE_ID_STDOUT))
-    {
-        fSignal = TRUE;
-    }
-    else if (   (uWaitFlags & ProcessWaitForFlag_StdErr)
-             && (dataCb.uHandle == OUTPUT_HANDLE_ID_STDERR))
-    {
-        fSignal = TRUE;
-    }
-
-    if (fSignal)
-    {
-        int rc2 = VINF_SUCCESS;
-        if (dataCb.uHandle == OUTPUT_HANDLE_ID_STDOUT)
-            rc2 = signalWaiters(ProcessWaitResult_StdOut);
-        else if (dataCb.uHandle == OUTPUT_HANDLE_ID_STDERR)
-            rc2 = signalWaiters(ProcessWaitResult_StdErr);
-        if (RT_SUCCESS(vrc))
-            vrc = rc2;
-    }
+    com::SafeArray<BYTE> data((size_t)dataCb.cbData);
+    data.initFrom((BYTE*)dataCb.pvData, dataCb.cbData);
+
+    fireGuestProcessOutputEvent(mEventSource, mSession, this,
+                                mData.mPID, dataCb.uHandle, ComSafeArrayAsInParam(data));
 
     LogFlowFuncLeaveRC(vrc);
@@ -855,4 +732,6 @@
     /* pcbRead is optional. */
 
+    /** @todo Validate uHandle. */
+
     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
@@ -866,31 +745,10 @@
     }
 
-    int vrc = VINF_SUCCESS;
-
-    GuestCtrlCallback *pCallbackRead = NULL;
-    try
-    {
-        pCallbackRead = new GuestCtrlCallback();
-    }
-    catch(std::bad_alloc &)
-    {
-        vrc = VERR_NO_MEMORY;
-    }
-
-    /* Create callback and add it to the map. */
-    uint32_t uContextID = 0;
+    uint32_t uContextID;
+    int vrc = generateContextID(mSession->getId(), mObjectID,
+                                &uContextID);
     if (RT_SUCCESS(vrc))
     {
-        vrc = pCallbackRead->Init(CALLBACKTYPE_PROC_OUTPUT);
-        if (RT_SUCCESS(vrc))
-            vrc = callbackAdd(pCallbackRead, &uContextID);
-    }
-
-    alock.release(); /* Drop the write lock again. */
-
-    if (RT_SUCCESS(vrc))
-    {
-        VBOXHGCMSVCPARM paParms[5];
-
+        VBOXHGCMSVCPARM paParms[8];
         int i = 0;
         paParms[i++].setUInt32(uContextID);
@@ -900,58 +758,13 @@
 
         vrc = sendCommand(HOST_EXEC_GET_OUTPUT, i, paParms);
-        if (RT_FAILURE(vrc))
-        {
-            int rc2 = setProcessStatus(ProcessStatus_Error, vrc);
-            AssertRC(rc2);
-        }
     }
 
     if (RT_SUCCESS(vrc))
     {
-        /*
-         * Let's wait for the process being started.
-         * Note: Be sure not keeping a AutoRead/WriteLock here.
-         */
-        LogFlowThisFunc(("Waiting for callback (%RU32ms) ...\n", uTimeoutMS));
-        vrc = pCallbackRead->Wait(uTimeoutMS);
-        if (RT_SUCCESS(vrc)) /* Wait was successful, check for supplied information. */
-        {
-            int guestRc = pCallbackRead->GetResultCode();
-            LogFlowThisFunc(("Callback returned rc=%Rrc, cbData=%RU32\n", guestRc, pCallbackRead->GetDataSize()));
-
-            if (RT_SUCCESS(guestRc))
-            {
-                Assert(pCallbackRead->GetDataSize() == sizeof(CALLBACKDATA_PROC_OUTPUT));
-                PCALLBACKDATA_PROC_OUTPUT pData = (PCALLBACKDATA_PROC_OUTPUT)pCallbackRead->GetDataRaw();
-                AssertPtr(pData);
-
-                size_t cbRead = pData->cbData;
-                if (cbRead)
-                {
-                    Assert(cbData >= cbRead);
-                    memcpy(pvData, pData->pvData, cbRead);
-                }
-
-                LogFlowThisFunc(("cbRead=%RU32\n", cbRead));
-
-                if (pcbRead)
-                    *pcbRead = cbRead;
-            }
-            else
-                vrc = VERR_GSTCTL_GUEST_ERROR;
-
-            if (pGuestRc)
-                *pGuestRc = guestRc;
-            LogFlowThisFunc(("Callback returned rc=%Rrc\n", guestRc));
-        }
-    }
-
-    alock.acquire();
-
-    int rc2 = callbackRemove(uContextID);
-    if (RT_SUCCESS(vrc))
-        vrc = rc2;
-
-    callbackDelete(pCallbackRead);
+        alock.release(); /* Drop the write lock before waiting. */
+
+        vrc = waitForOutput(uHandle, uTimeoutMS,
+                            pvData, cbData, pcbRead);
+    }
 
     LogFlowFuncLeaveRC(vrc);
@@ -970,11 +783,26 @@
         /* Do not allow overwriting an already set error. If this happens
          * this means we forgot some error checking/locking somewhere. */
-        AssertMsg(RT_SUCCESS(mData.mRC), ("Guest rc already set (to %Rrc)\n", mData.mRC));
+        //AssertMsg(RT_SUCCESS(mData.mRC), ("Guest rc already set (to %Rrc)\n", mData.mRC));
     }
     else
         AssertMsg(RT_SUCCESS(procRc), ("Guest rc must not be an error (%Rrc)\n", procRc));
 
-    mData.mStatus = procStatus;
-    mData.mRC     = procRc;
+    if (mData.mStatus != procStatus)
+    {
+        mData.mStatus = procStatus;
+        mData.mRC     = procRc;
+
+        ComObjPtr<GuestErrorInfo> errorInfo;
+        HRESULT hr = errorInfo.createObject();
+        ComAssertComRC(hr);
+        if (RT_FAILURE(mData.mRC))
+        {
+            int rc2 = errorInfo->init(mData.mRC, guestErrorToString(mData.mRC));
+            AssertRC(rc2);
+        }
+
+        fireGuestProcessStateChangedEvent(mEventSource, mSession, this,
+                                          mData.mPID, mData.mStatus, errorInfo);
+    }
 
     return VINF_SUCCESS;
@@ -988,18 +816,4 @@
 
     return pInterface->setError(VBOX_E_IPRT_ERROR, GuestProcess::guestErrorToString(guestRc).c_str());
-}
-
-int GuestProcess::signalWaiters(ProcessWaitResult_T enmWaitResult, int rc /*= VINF_SUCCESS */)
-{
-    LogFlowThisFunc(("enmWaitResult=%d, rc=%Rrc, mWaitCount=%RU32, mWaitEvent=%p\n",
-                     enmWaitResult, rc, mData.mWaitCount, mData.mWaitEvent));
-
-    /* Note: No write locking here -- already done in the caller. */
-
-    int vrc = VINF_SUCCESS;
-    if (mData.mWaitEvent)
-        vrc = mData.mWaitEvent->Signal(enmWaitResult, rc);
-    LogFlowFuncLeaveRC(vrc);
-    return vrc;
 }
 
@@ -1013,30 +827,12 @@
     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
-    int vrc = VINF_SUCCESS;
-    uint32_t uContextID = 0;
-
-    GuestCtrlCallback *pCallbackStart = NULL;
-    try
-    {
-        pCallbackStart = new GuestCtrlCallback();
-    }
-    catch(std::bad_alloc &)
-    {
-        vrc = VERR_NO_MEMORY;
-    }
-
+    mData.mStatus = ProcessStatus_Starting;
+
+    uint32_t uContextID;
+    int vrc = generateContextID(mSession->getId(), mObjectID,
+                                &uContextID);
     if (RT_SUCCESS(vrc))
     {
-        mData.mStatus = ProcessStatus_Starting;
-
-        /* Create callback and add it to the map. */
-        vrc = pCallbackStart->Init(CALLBACKTYPE_PROC_STATUS);
-        if (RT_SUCCESS(vrc))
-            vrc = callbackAdd(pCallbackStart, &uContextID);
-    }
-
-    if (RT_SUCCESS(vrc))
-    {
-        GuestSession *pSession = mObject.mSession;
+        GuestSession *pSession = mSession;
         AssertPtr(pSession);
 
@@ -1088,6 +884,6 @@
         if (RT_SUCCESS(vrc))
         {
-            AssertPtr(mObject.mSession);
-            uint32_t uProtocol = mObject.mSession->getProtocolVersion();
+            AssertPtr(mSession);
+            uint32_t uProtocol = mSession->getProtocolVersion();
 
             /* Prepare HGCM call. */
@@ -1151,34 +947,8 @@
         if (RT_SUCCESS(vrc))
         {
-            /*
-             * Let's wait for the process being started.
-             * Note: Be sure not keeping a AutoRead/WriteLock here.
-             */
-            LogFlowThisFunc(("Waiting for callback (%RU32ms) ...\n", uTimeoutMS));
-            vrc = pCallbackStart->Wait(uTimeoutMS);
-            if (RT_SUCCESS(vrc)) /* Wait was successful, check for supplied information. */
-            {
-                int guestRc = pCallbackStart->GetResultCode();
-                if (RT_SUCCESS(guestRc))
-                {
-                    /* Nothing to do here right now. */
-                }
-                else
-                    vrc = VERR_GSTCTL_GUEST_ERROR;
-
-                if (pGuestRc)
-                    *pGuestRc = guestRc;
-                LogFlowThisFunc(("Callback returned rc=%Rrc\n", guestRc));
-            }
-        }
-
-        AutoWriteLock awlock(this COMMA_LOCKVAL_SRC_POS);
-
-        int rc2 = callbackRemove(uContextID);
-        if (RT_SUCCESS(vrc))
-            vrc = rc2;
-    }
-
-    callbackDelete(pCallbackStart);
+            vrc = waitForStatusChange(ProcessWaitForFlag_Start, 30 * 1000 /* Timeout */,
+                                      NULL /* Process status */, pGuestRc);
+        }
+    }
 
     LogFlowFuncLeaveRC(vrc);
@@ -1245,5 +1015,6 @@
     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
-    if (mObject.mSession->getProtocolVersion() < 2)
+    AssertPtr(mSession);
+    if (mSession->getProtocolVersion() < 99)
         return VERR_NOT_SUPPORTED;
 
@@ -1251,65 +1022,25 @@
         return VINF_SUCCESS; /* Nothing to do (anymore). */
 
-    int vrc = VINF_SUCCESS;
-
-    GuestCtrlCallback *pCallbackTerminate = NULL;
-    try
-    {
-        pCallbackTerminate = new GuestCtrlCallback();
-    }
-    catch(std::bad_alloc &)
-    {
-        vrc = VERR_NO_MEMORY;
-    }
-
-    /* Create callback and add it to the map. */
     uint32_t uContextID;
+    int vrc = generateContextID(mSession->getId(), mObjectID,
+                                &uContextID);
     if (RT_SUCCESS(vrc))
-        vrc = callbackAdd(pCallbackTerminate, &uContextID);
-
-    alock.release(); /* Drop the write lock again. */
-
-    if (RT_SUCCESS(vrc))
-    {
-        VBOXHGCMSVCPARM paParms[5];
-
+    {
+        VBOXHGCMSVCPARM paParms[4];
         int i = 0;
         paParms[i++].setUInt32(uContextID);
         paParms[i++].setUInt32(mData.mPID);
 
-        //vrc = sendCommand(HOST_EXEC_TERMINATE, i, paParms);
+        vrc = sendCommand(HOST_EXEC_TERMINATE, i, paParms);
     }
 
     if (RT_SUCCESS(vrc))
     {
-        /*
-         * Let's wait for the process being terminated.
-         * Note: Be sure not keeping a AutoRead/WriteLock here.
-         */
-        LogFlowThisFunc(("Waiting for callback (30s) ...\n"));
-        vrc = pCallbackTerminate->Wait(30 * 1000);
-        if (RT_SUCCESS(vrc)) /* Wait was successful, check for supplied information. */
-        {
-            int guestRc = pCallbackTerminate->GetResultCode();
-            if (RT_SUCCESS(guestRc))
-            {
-                /* Nothing to do here right now. */
-            }
-            else
-                vrc = VERR_GSTCTL_GUEST_ERROR;
-
-            if (pGuestRc)
-                *pGuestRc = guestRc;
-            LogFlowThisFunc(("Callback returned rc=%Rrc\n", guestRc));
-        }
-    }
-
-    alock.acquire();
-
-    int rc2 = callbackRemove(uContextID);
-    if (RT_SUCCESS(vrc))
-        vrc = rc2;
-
-    callbackDelete(pCallbackTerminate);
+        alock.release(); /* Drop the write lock before waiting. */
+
+        vrc = waitForStatusChange(ProcessWaitForFlag_Terminate,
+                                  30 * 1000 /* 30s timeout */,
+                                  NULL /* ProcessStatus */, pGuestRc);
+    }
 
     LogFlowFuncLeaveRC(vrc);
@@ -1323,6 +1054,6 @@
     AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
 
-    LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, pGuestRc=%p\n",
-                     fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, pGuestRc));
+    /*LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, pGuestRc=%p\n",
+                     fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, pGuestRc));*/
 
     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
@@ -1419,6 +1150,9 @@
 
     /* Filter out waits which are *not* supported using
-     * older guest control Guest Additions. */
-    if (mObject.mSession->getProtocolVersion() < 2)
+     * older guest control Guest Additions.
+     *
+     ** @todo ProcessWaitForFlag_Std* flags are not implemented yet.
+     */
+    if (mSession->getProtocolVersion() < 99)
     {
         if (   waitResult == ProcessWaitResult_None
@@ -1447,51 +1181,430 @@
     }
 
-    if (mData.mWaitCount > 0) /* We only support one waiting caller a time at the moment. */
-        return VERR_ALREADY_EXISTS;
-    mData.mWaitCount++;
-
-    int vrc = VINF_SUCCESS;
-    try
-    {
-        Assert(mData.mWaitEvent == NULL);
-        mData.mWaitEvent = new GuestProcessWaitEvent(fWaitFlags);
-    }
-    catch(std::bad_alloc &)
-    {
-        vrc = VERR_NO_MEMORY;
-    }
-
+    alock.release(); /* Release lock before waiting. */
+
+    ProcessStatus_T processStatus;
+    int vrc = waitForStatusChange(fWaitFlags, uTimeoutMS, &processStatus, pGuestRc);
     if (RT_SUCCESS(vrc))
     {
-        GuestProcessWaitEvent *pEvent = mData.mWaitEvent;
-        AssertPtr(pEvent);
-
-        alock.release(); /* Release lock before waiting. */
-
-        vrc = pEvent->Wait(uTimeoutMS);
-        LogFlowThisFunc(("Waiting completed with rc=%Rrc\n", vrc));
-        if (RT_SUCCESS(vrc))
-        {
-            waitResult = pEvent->GetWaitResult();
-            int guestRc = pEvent->GetWaitRc();
-            if (RT_FAILURE(guestRc))
-                vrc = VERR_GSTCTL_GUEST_ERROR;
-
-            LogFlowThisFunc(("Waiting event returned rc=%Rrc\n", guestRc));
-
-            if (pGuestRc)
-                *pGuestRc = guestRc;
-        }
-
-        alock.acquire(); /* Get the lock again. */
-
-        /* Note: The caller always is responsible of deleting the
-         *       stuff it created before. See close() for more information. */
-        delete mData.mWaitEvent;
-        mData.mWaitEvent = NULL;
-    }
-
-    Assert(mData.mWaitCount);
-    mData.mWaitCount--;
+        switch (processStatus)
+        {
+            case ProcessStatus_Started:
+                waitResult = ProcessWaitResult_Start;
+                break;
+
+            case ProcessStatus_TerminatedNormally:
+            case ProcessStatus_TerminatedAbnormally:
+            case ProcessStatus_TerminatedSignal:
+                waitResult = ProcessWaitResult_Terminate;
+                break;
+
+            case ProcessStatus_TimedOutKilled:
+            case ProcessStatus_TimedOutAbnormally:
+                waitResult = ProcessWaitResult_Timeout;
+                break;
+
+            case ProcessStatus_Down:
+                waitResult = ProcessWaitResult_Terminate;
+                break;
+
+            case ProcessStatus_Error:
+                waitResult = ProcessWaitResult_Error;
+                break;
+
+            default:
+                waitResult = ProcessWaitResult_Status;
+                break;
+        }
+    }
+
+    LogFlowFuncLeaveRC(vrc);
+    return vrc;
+}
+
+int GuestProcess::waitForInputNotify(uint32_t uHandle, uint32_t uTimeoutMS,
+                                     ProcessInputStatus_T *pInputStatus, size_t *pcbProcessed)
+{
+    int vrc;
+
+    /** @todo Parameter validation. */
+
+    ComPtr<IEventListener> pListener;
+    HRESULT hr = mEventSource->CreateListener(pListener.asOutParam());
+    if (SUCCEEDED(hr))
+    {
+        com::SafeArray <VBoxEventType_T> eventTypes(1);
+        eventTypes.push_back(VBoxEventType_OnGuestProcessInputNotify);
+        hr = mEventSource->RegisterListener(pListener, ComSafeArrayAsInParam(eventTypes), false);
+    }
+    else
+        vrc = VERR_COM_UNEXPECTED;
+
+    if (SUCCEEDED(hr))
+    {
+        LogFlowThisFunc(("Waiting for guest process input notify event (timeout=%RU32ms, handle=%RU32) ...\n",
+                         uTimeoutMS, uHandle));
+
+        vrc = VINF_SUCCESS;
+
+        uint64_t u64Started = RTTimeMilliTS();
+        bool fSignalled = false;
+        do
+        {
+            unsigned cMsWait;
+            if (uTimeoutMS == RT_INDEFINITE_WAIT)
+                cMsWait = 1000;
+            else
+            {
+                uint64_t cMsElapsed = RTTimeMilliTS() - u64Started;
+                if (cMsElapsed >= uTimeoutMS)
+                    break; /* timed out */
+                cMsWait = RT_MIN(1000, uTimeoutMS - (uint32_t)cMsElapsed);
+            }
+
+            ComPtr<IEvent> pEvent;
+            hr = mEventSource->GetEvent(pListener, cMsWait, pEvent.asOutParam());
+            if (   SUCCEEDED(hr)
+                && !pEvent.isNull())
+            {
+                VBoxEventType_T aType;
+                hr = pEvent->COMGETTER(Type)(&aType);
+                ComAssertComRC(hr);
+                switch (aType)
+                {
+                    case VBoxEventType_OnGuestProcessInputNotify:
+                    {
+                        ComPtr<IGuestProcessInputNotifyEvent> pOutputEvent = pEvent;
+                        Assert(!pOutputEvent.isNull());
+
+                        ComPtr<IGuestSession> pSession;
+                        pOutputEvent->COMGETTER(Session)(pSession.asOutParam());
+                        Assert(!pSession.isNull());
+                        ULONG uSessionID;
+                        hr = pSession->COMGETTER(Id)(&uSessionID);
+                        ComAssertComRC(hr);
+                        if (uSessionID != mSession->getId())
+                            continue; /* Only the session this process runs in is of interest. */
+
+                        ULONG uPID;
+                        hr = pOutputEvent->COMGETTER(Pid)(&uPID);
+                        ComAssertComRC(hr);
+                        if (uPID != mData.mPID)
+                            continue; /* Only the this process is of interest. */
+
+                        ULONG uHandleEvent;
+                        hr = pOutputEvent->COMGETTER(Handle)(&uHandleEvent);
+                        ComAssertComRC(hr);
+
+                        LogFlowThisFunc(("Got output event for process PID=%RU32, handle=%RU32 (session ID=%RU32)\n",
+                                         mData.mPID, uHandleEvent, mSession->getId()));
+
+                        bool fSignal = uHandleEvent == uHandle;
+                        if (!fSignal)
+                            continue;
+
+                        ProcessInputStatus_T inputStatus;
+                        hr = pOutputEvent->COMGETTER(Status)(&inputStatus);
+                        ComAssertComRC(hr);
+
+                        ULONG uProcessed;
+                        hr = pOutputEvent->COMGETTER(Processed)(&uProcessed);
+                        ComAssertComRC(hr);
+
+                        if (pInputStatus)
+                            *pInputStatus = inputStatus;
+                        if (pcbProcessed)
+                            *pcbProcessed = uProcessed;
+
+                        LogFlowThisFunc(("Input notify event for process PID=%RU32 (session ID=%RU32): %zubytes read\n",
+                                         uPID, mSession->getId(), uProcessed));
+
+                        fSignalled = true;
+                        break;
+                    }
+
+                    default:
+                         AssertMsgFailed(("Unhandled event type %ld\n", aType));
+                         break;
+                }
+            }
+
+        } while (!fSignalled);
+
+        if (   RT_SUCCESS(vrc)
+            && !fSignalled)
+        {
+            vrc = VERR_TIMEOUT;
+        }
+
+        mEventSource->UnregisterListener(pListener);
+    }
+    else
+        vrc = VERR_COM_UNEXPECTED;
+
+    LogFlowFuncLeaveRC(vrc);
+    return vrc;
+}
+
+int GuestProcess::waitForOutput(uint32_t uHandle, uint32_t uTimeoutMS,
+                                void *pvData, size_t cbData, size_t *pcbRead)
+{
+    int vrc;
+
+    /** @todo Parameter validation. */
+
+    ComPtr<IEventListener> pListener;
+    HRESULT hr = mEventSource->CreateListener(pListener.asOutParam());
+    if (SUCCEEDED(hr))
+    {
+        com::SafeArray <VBoxEventType_T> eventTypes(1);
+        eventTypes.push_back(VBoxEventType_OnGuestProcessOutput);
+        hr = mEventSource->RegisterListener(pListener, ComSafeArrayAsInParam(eventTypes), false);
+    }
+    else
+        vrc = VERR_COM_UNEXPECTED;
+
+    if (SUCCEEDED(hr))
+    {
+        LogFlowThisFunc(("Waiting for guest process output event (timeout=%RU32ms, handle=%RU32) ...\n",
+                         uTimeoutMS, uHandle));
+
+        vrc = VINF_SUCCESS;
+
+        uint64_t u64Started = RTTimeMilliTS();
+        bool fSignalled = false;
+        do
+        {
+            unsigned cMsWait;
+            if (uTimeoutMS == RT_INDEFINITE_WAIT)
+                cMsWait = 1000;
+            else
+            {
+                uint64_t cMsElapsed = RTTimeMilliTS() - u64Started;
+                if (cMsElapsed >= uTimeoutMS)
+                    break; /* timed out */
+                cMsWait = RT_MIN(1000, uTimeoutMS - (uint32_t)cMsElapsed);
+            }
+
+            ComPtr<IEvent> pEvent;
+            hr = mEventSource->GetEvent(pListener, cMsWait, pEvent.asOutParam());
+            if (   SUCCEEDED(hr)
+                && !pEvent.isNull())
+            {
+                VBoxEventType_T aType;
+                hr = pEvent->COMGETTER(Type)(&aType);
+                ComAssertComRC(hr);
+                switch (aType)
+                {
+                    case VBoxEventType_OnGuestProcessOutput:
+                    {
+                        ComPtr<IGuestProcessOutputEvent> pOutputEvent = pEvent;
+                        Assert(!pOutputEvent.isNull());
+
+                        ComPtr<IGuestSession> pSession;
+                        pOutputEvent->COMGETTER(Session)(pSession.asOutParam());
+                        Assert(!pSession.isNull());
+                        ULONG uSessionID;
+                        hr = pSession->COMGETTER(Id)(&uSessionID);
+                        ComAssertComRC(hr);
+                        if (uSessionID != mSession->getId())
+                            continue; /* Only the session this process runs in is of interest. */
+
+                        ULONG uPID;
+                        hr = pOutputEvent->COMGETTER(Pid)(&uPID);
+                        ComAssertComRC(hr);
+                        if (uPID != mData.mPID)
+                            continue; /* Only the this process is of interest. */
+
+                        ULONG uHandleEvent;
+                        hr = pOutputEvent->COMGETTER(Handle)(&uHandleEvent);
+                        ComAssertComRC(hr);
+
+                        LogFlowThisFunc(("Got output event for process PID=%RU32, handle=%RU32 (session ID=%RU32): %ld\n",
+                                         mData.mPID, uHandleEvent, mSession->getId()));
+
+                        bool fSignal = uHandleEvent == uHandle;
+                        if (!fSignal)
+                            continue;
+
+                        com::SafeArray <BYTE> data;
+                        hr = pOutputEvent->COMGETTER(Data)(ComSafeArrayAsOutParam(data));
+                        ComAssertComRC(hr);
+
+                        size_t cbRead = data.size();
+
+                        if (pvData)
+                        {
+                            if (cbRead < cbData)
+                                cbData = cbRead;
+                            memcpy(pvData, data.raw(), cbData);
+                        }
+
+                        if (pcbRead)
+                            *pcbRead = cbRead;
+
+                        LogFlowThisFunc(("Output event for process PID=%RU32 (session ID=%RU32): %zubytes read\n",
+                                         uPID, mSession->getId(), cbRead));
+
+                        fSignalled = true;
+                        break;
+                    }
+
+                    default:
+                         AssertMsgFailed(("Unhandled event type %ld\n", aType));
+                         break;
+                }
+            }
+
+        } while (!fSignalled);
+
+        if (   RT_SUCCESS(vrc)
+            && !fSignalled)
+        {
+            vrc = VERR_TIMEOUT;
+        }
+
+        mEventSource->UnregisterListener(pListener);
+    }
+    else
+        vrc = VERR_COM_UNEXPECTED;
+
+    LogFlowFuncLeaveRC(vrc);
+    return vrc;
+}
+
+int GuestProcess::waitForStatusChange(uint32_t fWaitFlags, uint32_t uTimeoutMS,
+                                      ProcessStatus_T *pProcessStatus, int *pGuestRc)
+{
+    int vrc;
+
+    ComPtr<IEventListener> pListener;
+    HRESULT hr = mEventSource->CreateListener(pListener.asOutParam());
+    if (SUCCEEDED(hr))
+    {
+        com::SafeArray <VBoxEventType_T> eventTypes(1);
+        eventTypes.push_back(VBoxEventType_OnGuestProcessStateChanged);
+        hr = mEventSource->RegisterListener(pListener, ComSafeArrayAsInParam(eventTypes), false);
+    }
+    else
+        vrc = VERR_COM_UNEXPECTED;
+
+    if (SUCCEEDED(hr))
+    {
+        LogFlowThisFunc(("Waiting for guest process state changed event (timeout=%RU32ms, flags=%x) ...\n",
+                         uTimeoutMS, fWaitFlags));
+
+        vrc = VINF_SUCCESS;
+
+        uint64_t u64Started = RTTimeMilliTS();
+        bool fSignalled = false;
+        do
+        {
+            unsigned cMsWait;
+            if (uTimeoutMS == RT_INDEFINITE_WAIT)
+                cMsWait = 1000;
+            else
+            {
+                uint64_t cMsElapsed = RTTimeMilliTS() - u64Started;
+                if (cMsElapsed >= uTimeoutMS)
+                    break; /* timed out */
+                cMsWait = RT_MIN(1000, uTimeoutMS - (uint32_t)cMsElapsed);
+            }
+
+            ComPtr<IEvent> pEvent;
+            hr = mEventSource->GetEvent(pListener, cMsWait, pEvent.asOutParam());
+            if (   SUCCEEDED(hr)
+                && !pEvent.isNull())
+            {
+                VBoxEventType_T aType;
+                hr = pEvent->COMGETTER(Type)(&aType);
+                ComAssertComRC(hr);
+                switch (aType)
+                {
+                    case VBoxEventType_OnGuestProcessStateChanged:
+                    {
+                        ComPtr<IGuestProcessStateChangedEvent> pChangedEvent = pEvent;
+                        Assert(!pChangedEvent.isNull());
+
+                        ComPtr<IGuestSession> pSession;
+                        pChangedEvent->COMGETTER(Session)(pSession.asOutParam());
+                        Assert(!pSession.isNull());
+                        ULONG uSessionID;
+                        hr = pSession->COMGETTER(Id)(&uSessionID);
+                        ComAssertComRC(hr);
+                        if (uSessionID != mSession->getId())
+                            continue; /* Only the session this process runs in is of interest. */
+
+                        ULONG uPID;
+                        hr = pChangedEvent->COMGETTER(Pid)(&uPID);
+                        ComAssertComRC(hr);
+                        if (uPID != mData.mPID)
+                            continue; /* Only the this process is of interest. */
+
+                        ProcessStatus_T processStatus;
+                        pChangedEvent->COMGETTER(Status)(&processStatus);
+                        if (pProcessStatus)
+                            *pProcessStatus = processStatus;
+
+                        LogFlowThisFunc(("Got status changed event for process PID=%RU32 (session ID=%RU32): %ld\n",
+                                         mData.mPID, mSession->getId(), processStatus));
+
+                        bool fSignal = false;
+                        if (fWaitFlags)
+                        {
+                            switch (processStatus)
+                            {
+                                case ProcessStatus_Started:
+                                    fSignal = (fWaitFlags & ProcessWaitForFlag_Start);
+                                    break;
+
+                                default:
+                                    fSignal = true;
+                                    break;
+                            }
+                        }
+                        else
+                            fSignal = true;
+
+                        if (!fSignal)
+                            continue;
+
+                        ComPtr<IGuestErrorInfo> errorInfo;
+                        hr = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam());
+                        ComAssertComRC(hr);
+
+                        LONG lGuestRc;
+                        hr = errorInfo->COMGETTER(Result)(&lGuestRc);
+                        ComAssertComRC(hr);
+                        if (RT_FAILURE((int)lGuestRc))
+                            vrc = VERR_GSTCTL_GUEST_ERROR;
+                        if (pGuestRc)
+                            *pGuestRc = (int)lGuestRc;
+
+                        LogFlowThisFunc(("Status changed event for process PID=%RU32 (session ID=%RU32): %ld (%Rrc)\n",
+                                         uPID, mSession->getId(), processStatus,
+                                         RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc));
+
+                        fSignalled = true;
+                        break;
+                    }
+
+                    default:
+                         AssertMsgFailed(("Unhandled event type %ld\n", aType));
+                         break;
+                }
+            }
+
+        } while (!fSignalled);
+
+        if (   RT_SUCCESS(vrc)
+            && !fSignalled)
+        {
+            vrc = VERR_TIMEOUT;
+        }
+
+        mEventSource->UnregisterListener(pListener);
+    }
+    else
+        vrc = VERR_COM_UNEXPECTED;
 
     LogFlowFuncLeaveRC(vrc);
@@ -1517,27 +1630,7 @@
     }
 
-    int vrc = VINF_SUCCESS;
-
-    GuestCtrlCallback *pCallbackWrite = NULL;
-    try
-    {
-        pCallbackWrite = new GuestCtrlCallback();
-    }
-    catch(std::bad_alloc &)
-    {
-        vrc = VERR_NO_MEMORY;
-    }
-
-    /* Create callback and add it to the map. */
-    uint32_t uContextID = 0;
-    if (RT_SUCCESS(vrc))
-    {
-        vrc = pCallbackWrite->Init(CALLBACKTYPE_PROC_INPUT);
-        if (RT_SUCCESS(vrc))
-            vrc = callbackAdd(pCallbackWrite, &uContextID);
-    }
-
-    alock.release(); /* Drop the write lock again. */
-
+    uint32_t uContextID;
+    int vrc = generateContextID(mSession->getId(), mObjectID,
+                                &uContextID);
     if (RT_SUCCESS(vrc))
     {
@@ -1552,73 +1645,22 @@
 
         vrc = sendCommand(HOST_EXEC_SET_INPUT, i, paParms);
-        if (RT_FAILURE(vrc))
-        {
-            int rc2 = setProcessStatus(ProcessStatus_Error, vrc);
-            AssertRC(rc2);
-        }
     }
 
     if (RT_SUCCESS(vrc))
     {
-        /*
-         * Let's wait for the process being started.
-         * Note: Be sure not keeping a AutoRead/WriteLock here.
-         */
-        LogFlowThisFunc(("Waiting for callback (%RU32ms) ...\n", uTimeoutMS));
-        vrc = pCallbackWrite->Wait(uTimeoutMS);
-        if (RT_SUCCESS(vrc)) /* Wait was successful, check for supplied information. */
-        {
-            int guestRc = pCallbackWrite->GetResultCode();
-            if (RT_SUCCESS(guestRc))
-            {
-                Assert(pCallbackWrite->GetDataSize() == sizeof(CALLBACKDATA_PROC_INPUT));
-                PCALLBACKDATA_PROC_INPUT pData = (PCALLBACKDATA_PROC_INPUT)pCallbackWrite->GetDataRaw();
-                AssertPtr(pData);
-
-                uint32_t cbWritten = 0;
-                switch (pData->uStatus)
-                {
-                    case INPUT_STS_WRITTEN:
-                        cbWritten = pData->uProcessed;
-                        break;
-
-                    case INPUT_STS_ERROR:
-                        vrc = pData->uFlags; /** @todo Fix int vs. uint32_t! */
-                        break;
-
-                    case INPUT_STS_TERMINATED:
-                        vrc = VERR_CANCELLED;
-                        break;
-
-                    case INPUT_STS_OVERFLOW:
-                        vrc = VERR_BUFFER_OVERFLOW;
-                        break;
-
-                    default:
-                        /* Silently skip unknown errors. */
-                        break;
-                }
-
-                LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten));
-
-                if (puWritten)
-                    *puWritten = cbWritten;
-            }
-            else
-                vrc = VERR_GSTCTL_GUEST_ERROR;
-
-            if (pGuestRc)
-                *pGuestRc = guestRc;
-            LogFlowThisFunc(("Callback returned rc=%Rrc\n", guestRc));
-        }
-    }
-
-    alock.acquire();
-
-    int rc2 = callbackRemove(uContextID);
-    if (RT_SUCCESS(vrc))
-        vrc = rc2;
-
-    callbackDelete(pCallbackWrite);
+        alock.release(); /* Drop the write lock before waiting. */
+
+        ProcessInputStatus_T inputStatus;
+        size_t cbProcessed;
+        vrc = waitForInputNotify(uHandle, uTimeoutMS, &inputStatus, &cbProcessed);
+        if (RT_SUCCESS(vrc))
+        {
+            /** @todo Set guestRc. */
+
+            if (puWritten)
+                *puWritten = cbProcessed;
+        }
+        /** @todo Error handling. */
+    }
 
     LogFlowFuncLeaveRC(vrc);
@@ -1713,6 +1755,6 @@
     }
 
-    AssertPtr(mObject.mSession);
-    mObject.mSession->processRemoveFromList(this);
+    AssertPtr(mSession);
+    mSession->processRemoveFromList(this);
 
     /*
Index: /trunk/src/VBox/Main/src-client/GuestSessionImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/GuestSessionImpl.cpp	(revision 45414)
+++ /trunk/src/VBox/Main/src-client/GuestSessionImpl.cpp	(revision 45415)
@@ -22,4 +22,5 @@
 *******************************************************************************/
 #include "GuestImpl.h"
+#include "GuestErrorInfoImpl.h"
 #include "GuestSessionImpl.h"
 #include "GuestCtrlImplPrivate.h"
@@ -28,12 +29,15 @@
 #include "AutoCaller.h"
 #include "ProgressImpl.h"
+#include "VBoxEvents.h"
 #include "VMMDev.h"
 
 #include <memory> /* For auto_ptr. */
 
+#include <iprt/cpp/utils.h> /* For unconst(). */
 #include <iprt/env.h>
 #include <iprt/file.h> /* For CopyTo/From. */
 
 #include <VBox/com/array.h>
+#include <VBox/com/listeners.h>
 #include <VBox/version.h>
 
@@ -91,7 +95,4 @@
     mData.mRC = VINF_SUCCESS;
     mData.mStatus = GuestSessionStatus_Undefined;
-
-    mData.mWaitCount = 0;
-    mData.mWaitEvent = NULL;
 
     return BaseFinalConstruct();
@@ -128,5 +129,5 @@
     AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
 
-    mData.mParent = pGuest;
+    mParent = pGuest;
 
     /* Copy over startup info. */
@@ -149,4 +150,13 @@
     if (RT_SUCCESS(rc))
     {
+        unconst(mEventSource).createObject();
+        Assert(!mEventSource.isNull());
+        HRESULT hr = mEventSource->init(static_cast<IGuestSession*>(this));
+        if (FAILED(hr))
+            rc = VERR_COM_UNEXPECTED;
+    }
+
+    if (RT_SUCCESS(rc))
+    {
         /* Confirm a successful initialization when it's the case. */
         autoInitSpan.setSucceeded();
@@ -174,5 +184,5 @@
     int rc = VINF_SUCCESS;
 
-#ifndef VBOX_WITH_GUEST_CONTROL
+#ifdef VBOX_WITH_GUEST_CONTROL
     LogFlowThisFunc(("Closing directories (%RU64 total)\n",
                      mData.mDirectories.size()));
@@ -180,9 +190,9 @@
          itDirs != mData.mDirectories.end(); ++itDirs)
     {
-#ifdef DEBUG
+# ifdef DEBUG
         ULONG cRefs = (*itDirs)->AddRef();
-        LogFlowThisFunc(("pFile=%p, cRefs=%RU32\n", (*itDirs), cRefs));
+        LogFlowThisFunc(("pFile=%p, cRefs=%RU32\n", (*itDirs), cRefs - 1));
         (*itDirs)->Release();
-#endif
+# endif
         (*itDirs)->uninit();
     }
@@ -194,10 +204,10 @@
          itFiles != mData.mFiles.end(); ++itFiles)
     {
-#ifdef DEBUG
-        ULONG cRefs = (*itFiles)->AddRef();
-        LogFlowThisFunc(("pFile=%p, cRefs=%RU32\n", (*itFiles), cRefs));
-        (*itFiles)->Release();
-#endif
-        (*itFiles)->uninit();
+# ifdef DEBUG
+        ULONG cRefs = itFiles->second->AddRef();
+        LogFlowThisFunc(("pFile=%p, cRefs=%RU32\n", (*itFiles), cRefs - 1));
+        itFiles->second->Release();
+# endif
+        itFiles->second->uninit();
     }
     mData.mFiles.clear();
@@ -208,9 +218,9 @@
          itProcs != mData.mProcesses.end(); ++itProcs)
     {
-#ifdef DEBUG
+# ifdef DEBUG
         ULONG cRefs = itProcs->second->AddRef();
-        LogFlowThisFunc(("pProcess=%p, cRefs=%RU32\n", itProcs->second, cRefs));
+        LogFlowThisFunc(("pProcess=%p, cRefs=%RU32\n", itProcs->second, cRefs - 1));
         itProcs->second->Release();
-#endif
+# endif
         itProcs->second->uninit();
     }
@@ -218,5 +228,8 @@
 
     LogFlowThisFunc(("mNumObjects=%RU32\n", mData.mNumObjects));
-#endif
+
+    unconst(mEventSource).setNull();
+
+#endif /* VBOX_WITH_GUEST_CONTROL */
     LogFlowFuncLeaveRC(rc);
 }
@@ -495,4 +508,17 @@
 }
 
+STDMETHODIMP GuestSession::COMGETTER(EventSource)(IEventSource ** aEventSource)
+{
+    CheckComArgOutPointerValid(aEventSource);
+
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    // no need to lock - lifetime constant
+    mEventSource.queryInterfaceTo(aEventSource);
+
+    return S_OK;
+}
+
 // private methods
 ///////////////////////////////////////////////////////////////////////////////
@@ -501,4 +527,6 @@
 {
     LogFlowThisFunc(("uFlags=%x, uTimeoutMS=%RU32\n", uFlags, uTimeoutMS));
+
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
     /* Legacy Guest Additions don't support opening dedicated
@@ -512,57 +540,33 @@
     /** @todo uFlags validation. */
 
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    /* Destroy a pending callback request. */
-    mData.mCallback.Destroy();
-
-    int vrc = mData.mCallback.Init(CALLBACKTYPE_SESSION_NOTIFY);
-
-    alock.release(); /* Drop the write lock again. */
-
+    if (mData.mStatus != GuestSessionStatus_Started)
+        return VINF_SUCCESS;
+
+    uint32_t uContextID;
+    int vrc = generateContextID(mData.mSession.mID, 0 /* Object ID */,
+                                &uContextID);
     if (RT_SUCCESS(vrc))
     {
-        /* The context ID only contains this session's ID; all other
-         * parameters like object and the count itself are not needed
-         * and therefore 0. */
-        uint32_t uContextID = VBOX_GUESTCTRL_CONTEXTID_MAKE(mData.mSession.mID /* Session ID */,
-                                                            0 /* Object */, 0 /* Count */);
-
-        VBOXHGCMSVCPARM paParms[4];
-
-        int i = 0;
-        paParms[i++].setUInt32(uContextID);
-        paParms[i++].setUInt32(uFlags);
-
-        vrc = sendCommand(HOST_SESSION_CLOSE, i, paParms);
-    }
-
-    if (RT_SUCCESS(vrc))
-    {
-        /*
-         * Let's wait for the process being started.
-         * Note: Be sure not keeping a AutoRead/WriteLock here.
-         */
-        LogFlowThisFunc(("Waiting for callback (%RU32ms) ...\n", uTimeoutMS));
-        vrc = mData.mCallback.Wait(uTimeoutMS);
-        if (RT_SUCCESS(vrc)) /* Wait was successful, check for supplied information. */
-        {
-            int guestRc = mData.mCallback.GetResultCode();
-            if (RT_SUCCESS(guestRc))
-            {
-                /* Nothing to do here right now. */
-            }
-            else
-                vrc = VERR_GSTCTL_GUEST_ERROR;
-
-            if (pGuestRc)
-                *pGuestRc = guestRc;
-            LogFlowThisFunc(("Callback returned rc=%Rrc\n", guestRc));
-        }
-    }
-
-    AutoWriteLock awlock(this COMMA_LOCKVAL_SRC_POS);
-
-    mData.mCallback.Destroy();
+        if (RT_SUCCESS(vrc))
+        {
+            LogFlowThisFunc(("Sending closing request to guest session ID=%RU32, uFlags=%x\n",
+                             mData.mSession.mID, uFlags));
+
+            VBOXHGCMSVCPARM paParms[4];
+            int i = 0;
+            paParms[i++].setUInt32(uContextID);
+            paParms[i++].setUInt32(uFlags);
+
+            vrc = sendCommand(HOST_SESSION_CLOSE, i, paParms);
+        }
+
+        if (RT_SUCCESS(vrc))
+        {
+            alock.release(); /* Drop the write lock before waiting. */
+
+            vrc = waitForStateChange(GuestSessionWaitForFlag_Terminate, uTimeoutMS,
+                                     NULL /* Session status */, pGuestRc);
+        }
+    }
 
     LogFlowFuncLeaveRC(vrc);
@@ -813,5 +817,5 @@
 
 #ifdef DEBUG
-    LogFlowThisFunc(("ID=%RU32, uContextID=%RU32, uFunction=%RU32, pSvcCb=%p\n",
+    LogFlowThisFunc(("sessionID=%RU32, CID=%RU32, uFunction=%RU32, pSvcCb=%p\n",
                      mData.mSession.mID, pCbCtx->uContextID, pCbCtx->uFunction, pSvcCb));
 #endif
@@ -821,11 +825,10 @@
     {
         case GUEST_DISCONNECTED:
-            /** @todo Handle closing all processes. */
+            /** @todo Handle closing all guest objects. */
             break;
 
         case GUEST_SESSION_NOTIFY:
         {
-            rc = onSessionStatusChange(pCbCtx,
-                                       &mData.mCallback, pSvcCb);
+            rc = onSessionStatusChange(pCbCtx, pSvcCb);
             break;
         }
@@ -960,5 +963,5 @@
         return VERR_COM_UNEXPECTED;
 
-    Console *pConsole = mData.mParent->getConsole();
+    Console *pConsole = mParent->getConsole();
     AssertPtr(pConsole);
 
@@ -1103,5 +1106,5 @@
 
         case VERR_MAX_PROCS_REACHED:
-            strError += Utf8StrFmt(tr("Maximum number of parallel guest processes has been reached"));
+            strError += Utf8StrFmt(tr("Maximum number of concurrent guest processes has been reached"));
             break;
 
@@ -1122,8 +1125,25 @@
 }
 
+/**
+ * Checks if this session is ready state where it can handle
+ * all session-bound actions (like guest processes, guest files).
+ * Only used by official API methods. Will set an external
+ * error when not ready.
+ */
+HRESULT GuestSession::isReadyExternal(void)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    /** @todo Be a bit more informative. */
+    if (mData.mStatus != GuestSessionStatus_Started)
+        return setError(E_UNEXPECTED, tr("Session is not in started state"));
+
+    return S_OK;
+}
+
 /** No locking! */
-int GuestSession::onSessionStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx,
-                                        GuestCtrlCallback *pCallback, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
-{
+int GuestSession::onSessionStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
+{
+    AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
     /* pCallback is optional. */
     AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
@@ -1137,8 +1157,10 @@
     pSvcCbData->mpaParms[2].getUInt32(&dataCb.uResult);
 
-    LogFlowThisFunc(("ID=%RU32, uType=%RU32, rc=%Rrc, pCallback=%p, pData=%p\n",
-                     mData.mSession.mID, dataCb.uType, dataCb.uResult, pCallback, pSvcCbData));
+    LogFlowThisFunc(("ID=%RU32, uType=%RU32, guestRc=%Rrc\n",
+                     mData.mSession.mID, dataCb.uType, dataCb.uResult));
 
     int vrc = VINF_SUCCESS;
+
+    GuestSessionStatus_T sessionStatus = GuestSessionStatus_Undefined;
 
     int guestRc = dataCb.uResult; /** @todo uint32_t vs. int. */
@@ -1146,9 +1168,9 @@
     {
         case GUEST_SESSION_NOTIFYTYPE_ERROR:
-            mData.mStatus = GuestSessionStatus_Error;
+            sessionStatus = GuestSessionStatus_Error;
             break;
 
         case GUEST_SESSION_NOTIFYTYPE_STARTED:
-            mData.mStatus = GuestSessionStatus_Started;
+            sessionStatus = GuestSessionStatus_Started;
             break;
 
@@ -1156,19 +1178,20 @@
         case GUEST_SESSION_NOTIFYTYPE_TES:
         case GUEST_SESSION_NOTIFYTYPE_TEA:
-            mData.mStatus = GuestSessionStatus_Terminated;
+            sessionStatus = GuestSessionStatus_Terminated;
             break;
 
         case GUEST_SESSION_NOTIFYTYPE_TOK:
-            mData.mStatus = GuestSessionStatus_TimedOutKilled;
+            sessionStatus = GuestSessionStatus_TimedOutKilled;
             break;
 
         case GUEST_SESSION_NOTIFYTYPE_TOA:
-            mData.mStatus = GuestSessionStatus_TimedOutAbnormally;
+            sessionStatus = GuestSessionStatus_TimedOutAbnormally;
             break;
 
         case GUEST_SESSION_NOTIFYTYPE_DWN:
-            mData.mStatus = GuestSessionStatus_Down;
+            sessionStatus = GuestSessionStatus_Down;
             break;
 
+        case GUEST_SESSION_NOTIFYTYPE_UNDEFINED:
         default:
             vrc = VERR_NOT_SUPPORTED;
@@ -1179,25 +1202,16 @@
     {
         if (RT_FAILURE(guestRc))
-            mData.mStatus = GuestSessionStatus_Error;
-    }
-    else if (vrc == VERR_NOT_SUPPORTED)
-    {
-        /* Also let the callback know. */
-        guestRc = VERR_NOT_SUPPORTED;
-    }
-
-    /*
-     * Now do the signalling stuff.
-     */
-    if (pCallback)
-    {
-        int rc2 = pCallback->SetData(&dataCb, sizeof(dataCb));
-        AssertRC(rc2);
-        rc2 = pCallback->Signal(guestRc);
-        AssertRC(rc2);
-    }
-
-    LogFlowThisFunc(("ID=%RU32, guestRc=%Rrc\n",
-                     mData.mSession.mID, guestRc));
+            sessionStatus = GuestSessionStatus_Error;
+    }
+
+    /* Set the session status. */
+    if (sessionStatus != GuestSessionStatus_Undefined)
+    {
+        int rc2 = setSessionStatus(sessionStatus, guestRc);
+        if (RT_SUCCESS(vrc))
+            vrc = rc2;
+    }
+
+    LogFlowThisFunc(("ID=%RU32, guestRc=%Rrc\n", mData.mSession.mID, guestRc));
 
     LogFlowFuncLeaveRC(vrc);
@@ -1209,6 +1223,7 @@
     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
-    LogFlowThisFunc(("uProtocolVersion=%RU32, openFlags=%x, openTimeoutMS=%RU32\n",
-                     mData.mProtocolVersion, mData.mSession.mOpenFlags, mData.mSession.mOpenTimeoutMS));
+    LogFlowThisFunc(("mID=%RU32, mName=%s, uProtocolVersion=%RU32, openFlags=%x, openTimeoutMS=%RU32\n",
+                     mData.mSession.mID, mData.mSession.mName.c_str(), mData.mProtocolVersion,
+                     mData.mSession.mOpenFlags, mData.mSession.mOpenTimeoutMS));
 
     /* Legacy Guest Additions don't support opening dedicated
@@ -1222,4 +1237,7 @@
     }
 
+    if (mData.mStatus != GuestSessionStatus_Undefined)
+        return VINF_SUCCESS;
+
     /** @todo mData.mSession.uFlags validation. */
 
@@ -1227,17 +1245,9 @@
     mData.mStatus = GuestSessionStatus_Starting;
 
-    /* Destroy a pending callback request. */
-    mData.mCallback.Destroy();
-
-    int vrc = mData.mCallback.Init(CALLBACKTYPE_SESSION_NOTIFY);
-
-    alock.release(); /* Drop the write lock again. */
-
+    uint32_t uContextID;
+    int vrc = generateContextID(mData.mSession.mID, 0 /* Object ID */,
+                                &uContextID);
     if (RT_SUCCESS(vrc))
     {
-        uint32_t uContextID =
-            VBOX_GUESTCTRL_CONTEXTID_MAKE(mData.mSession.mID /* Session ID */,
-                                          0 /* Object */, 0 /* Count */);
-
         VBOXHGCMSVCPARM paParms[8];
 
@@ -1258,30 +1268,9 @@
     if (RT_SUCCESS(vrc))
     {
-        /*
-         * Let's wait for the process being started.
-         * Note: Be sure not keeping a AutoRead/WriteLock here.
-         */
-        LogFlowThisFunc(("Waiting for callback (%RU32ms) ...\n", mData.mSession.mOpenTimeoutMS));
-        vrc = mData.mCallback.Wait(mData.mSession.mOpenTimeoutMS);
-        if (RT_SUCCESS(vrc)) /* Wait was successful, check for supplied information. */
-        {
-            int guestRc = mData.mCallback.GetResultCode();
-            if (RT_SUCCESS(guestRc))
-            {
-                /* Nothing to do here right now. */
-            }
-            else
-                vrc = VERR_GSTCTL_GUEST_ERROR;
-
-            if (pGuestRc)
-                *pGuestRc = guestRc;
-            LogFlowThisFunc(("Callback returned rc=%Rrc\n", guestRc));
-        }
-    }
-
-    alock.acquire();
-
-    /* Destroy callback. */
-    mData.mCallback.Destroy();
+        alock.release(); /* Drop write lock before waiting. */
+
+        vrc = waitForStateChange(GuestSessionWaitForFlag_Start,  30 * 1000 /* 30s timeout */,
+                                 NULL /* Session status */, pGuestRc);
+    }
 
     LogFlowFuncLeaveRC(vrc);
@@ -1344,5 +1333,7 @@
 int GuestSession::processRemoveFromList(GuestProcess *pProcess)
 {
-    LogFlowThisFuncEnter();
+    AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
+
+    LogFlowThisFunc(("pProcess=%p\n", pProcess));
 
     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
@@ -1352,9 +1343,10 @@
     ULONG uPID;
     HRESULT hr = pProcess->COMGETTER(PID)(&uPID);
+    ComAssertComRC(hr);
 
     LogFlowFunc(("Closing process (PID=%RU32) ...\n", uPID));
 
-    for (SessionProcesses::iterator itProcs = mData.mProcesses.begin();
-         itProcs != mData.mProcesses.end(); ++itProcs)
+    SessionProcesses::iterator itProcs = mData.mProcesses.begin();
+    while (itProcs != mData.mProcesses.end())
     {
         if (pProcess == itProcs->second)
@@ -1367,13 +1359,21 @@
 
             Assert(mData.mNumObjects);
-            LogFlowFunc(("Removing process (Session: %RU32) with process ID=%RU32, guest PID=%RU32 (now total %ld processes, %ld objects)\n",
-                         mData.mSession.mID, pCurProc->getObjectID(), uPID, mData.mProcesses.size() - 1, mData.mNumObjects - 1));
-
+            LogFlowFunc(("Removing process ID=%RU32 (Session: %RU32), guest PID=%RU32 (now total %ld processes, %ld objects)\n",
+                         pCurProc->getObjectID(), mData.mSession.mID, uPID, mData.mProcesses.size() - 1, mData.mNumObjects - 1));
+
+            LogFlowFunc(("1\n"));
             mData.mProcesses.erase(itProcs);
             mData.mNumObjects--;
 
+            LogFlowFunc(("2\n"));
+            fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, NULL /* Process */,
+                                            uPID, false /* Process unregistered */);
+            LogFlowFunc(("3\n"));
             rc = VINF_SUCCESS;
             break;
         }
+
+        LogFlowFunc(("4\n"));
+        itProcs++;
     }
 
@@ -1475,5 +1475,5 @@
         return VERR_COM_UNEXPECTED;
 
-    rc = pProcess->init(mData.mParent->getConsole() /* Console */, this /* Session */,
+    rc = pProcess->init(mParent->getConsole() /* Console */, this /* Session */,
                         uNewProcessID, procInfo);
     if (RT_FAILURE(rc))
@@ -1485,4 +1485,7 @@
     Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS);
 
+    fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProcess,
+                                    0 /* PID */, true /* Process registered */);
+
     LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %ld processes, %ld objects)\n",
                  mData.mSession.mID, uNewProcessID, mData.mProcesses.size(), mData.mNumObjects));
@@ -1537,5 +1540,5 @@
 
 #ifndef VBOX_GUESTCTRL_TEST_CASE
-    ComObjPtr<Console> pConsole = mData.mParent->getConsole();
+    ComObjPtr<Console> pConsole = mParent->getConsole();
     Assert(!pConsole.isNull());
 
@@ -1565,4 +1568,52 @@
 
     return pInterface->setError(VBOX_E_IPRT_ERROR, GuestSession::guestErrorToString(guestRc).c_str());
+}
+
+/* Does not do locking; caller is responsible for that! */
+int GuestSession::setSessionStatus(GuestSessionStatus_T sessionStatus, int sessionRc)
+{
+    LogFlowThisFunc(("oldStatus=%ld, newStatus=%ld, sessionRc=%Rrc\n",
+                     mData.mStatus, sessionStatus, sessionRc));
+
+    if (sessionStatus == GuestSessionStatus_Error)
+    {
+        AssertMsg(RT_FAILURE(sessionRc), ("Guest rc must be an error (%Rrc)\n", sessionRc));
+        /* Do not allow overwriting an already set error. If this happens
+         * this means we forgot some error checking/locking somewhere. */
+        AssertMsg(RT_SUCCESS(mData.mRC), ("Guest rc already set (to %Rrc)\n", mData.mRC));
+    }
+    else
+        AssertMsg(RT_SUCCESS(sessionRc), ("Guest rc must not be an error (%Rrc)\n", sessionRc));
+
+    if (mData.mStatus != sessionStatus)
+    {
+        mData.mStatus = sessionStatus;
+        mData.mRC     = sessionRc;
+
+        ComObjPtr<GuestErrorInfo> errorInfo;
+        HRESULT hr = errorInfo.createObject();
+        ComAssertComRC(hr);
+        int rc2 = errorInfo->init(sessionRc, guestErrorToString(sessionRc));
+        AssertRC(rc2);
+
+        fireGuestSessionStateChangedEvent(mEventSource, this,
+                                          mData.mSession.mID, sessionStatus, errorInfo);
+    }
+
+    return VINF_SUCCESS;
+}
+
+int GuestSession::signalWaiters(GuestSessionWaitResult_T enmWaitResult, int rc /*= VINF_SUCCESS */)
+{
+    /*LogFlowThisFunc(("enmWaitResult=%d, rc=%Rrc, mWaitCount=%RU32, mWaitEvent=%p\n",
+                     enmWaitResult, rc, mData.mWaitCount, mData.mWaitEvent));*/
+
+    /* Note: No write locking here -- already done in the caller. */
+
+    int vrc = VINF_SUCCESS;
+    /*if (mData.mWaitEvent)
+        vrc = mData.mWaitEvent->Signal(enmWaitResult, rc);*/
+    LogFlowFuncLeaveRC(vrc);
+    return vrc;
 }
 
@@ -1621,5 +1672,5 @@
      * This is done using the Guest Additions version
      */
-    ComObjPtr<Guest> pGuest = mData.mParent;
+    ComObjPtr<Guest> pGuest = mParent;
     Assert(!pGuest.isNull());
 
@@ -1647,6 +1698,6 @@
     AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
 
-    LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, pGuestRc=%p\n",
-                     fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, pGuestRc));
+    /*LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, pGuestRc=%p\n",
+                     fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, pGuestRc));*/
 
     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
@@ -1737,51 +1788,170 @@
     }
 
-    if (mData.mWaitCount > 0) /* We only support one waiting caller a time at the moment. */
-        return VERR_ALREADY_EXISTS;
-    mData.mWaitCount++;
-
-    int vrc = VINF_SUCCESS;
-    try
-    {
-        Assert(mData.mWaitEvent == NULL);
-        mData.mWaitEvent = new GuestSessionWaitEvent(fWaitFlags);
-    }
-    catch(std::bad_alloc &)
-    {
-        vrc = VERR_NO_MEMORY;
-    }
-
+    alock.release(); /* Release lock before waiting. */
+
+    GuestSessionStatus_T sessionStatus;
+    int vrc = waitForStateChange(fWaitFlags, uTimeoutMS, &sessionStatus, pGuestRc);
     if (RT_SUCCESS(vrc))
     {
-        GuestSessionWaitEvent *pEvent = mData.mWaitEvent;
-        AssertPtr(pEvent);
-
-        alock.release(); /* Release lock before waiting. */
-
-        vrc = pEvent->Wait(uTimeoutMS);
-        LogFlowThisFunc(("Waiting completed with rc=%Rrc\n", vrc));
-        if (RT_SUCCESS(vrc))
-        {
-            waitResult = pEvent->GetWaitResult();
-            int guestRc = pEvent->GetWaitRc();
-            if (RT_FAILURE(guestRc))
-                vrc = VERR_GSTCTL_GUEST_ERROR;
-
-            LogFlowThisFunc(("Waiting event returned rc=%Rrc\n", guestRc));
-
-            if (pGuestRc)
-                *pGuestRc = guestRc;
-        }
-
-        alock.acquire(); /* Get the lock again. */
-
-        /* Note: The caller always is responsible of deleting the
-         *       stuff it created before. See close() for more information. */
-        delete mData.mWaitEvent;
-        mData.mWaitEvent = NULL;
-    }
-
-    Assert(mData.mWaitCount);
-    mData.mWaitCount--;
+        switch (sessionStatus)
+        {
+            case GuestSessionStatus_Started:
+                waitResult = GuestSessionWaitResult_Start;
+                break;
+
+            case GuestSessionStatus_Terminated:
+                waitResult = GuestSessionWaitResult_Terminate;
+                break;
+
+            case GuestSessionStatus_TimedOutKilled:
+            case GuestSessionStatus_TimedOutAbnormally:
+                waitResult = GuestSessionWaitResult_Timeout;
+                break;
+
+            case GuestSessionStatus_Down:
+                waitResult = GuestSessionWaitResult_Terminate;
+                break;
+
+            case GuestSessionStatus_Error:
+                waitResult = GuestSessionWaitResult_Error;
+                break;
+
+            default:
+                waitResult = GuestSessionWaitResult_Status;
+                break;
+        }
+    }
+
+    LogFlowFuncLeaveRC(vrc);
+    return vrc;
+}
+
+int GuestSession::waitForStateChange(uint32_t fWaitFlags, uint32_t uTimeoutMS,
+                                     GuestSessionStatus_T *pSessionStatus, int *pGuestRc)
+{
+    /** @todo Param validation. */
+
+    int vrc;
+
+    ComPtr<IEventListener> pListener;
+    HRESULT hr = mEventSource->CreateListener(pListener.asOutParam());
+    if (SUCCEEDED(hr))
+    {
+        com::SafeArray <VBoxEventType_T> eventTypes(1);
+        eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
+        hr = mEventSource->RegisterListener(pListener, ComSafeArrayAsInParam(eventTypes), false);
+    }
+    else
+        vrc = VERR_COM_UNEXPECTED;
+
+    if (SUCCEEDED(hr))
+    {
+        LogFlowThisFunc(("Waiting for guest session state changed event (timeout=%RU32ms, flags=%x) ...\n",
+                         uTimeoutMS, fWaitFlags));
+
+        vrc = VINF_SUCCESS;
+
+        uint64_t u64Started = RTTimeMilliTS();
+        bool fSignalled = false;
+        do
+        {
+            unsigned cMsWait;
+            if (uTimeoutMS == RT_INDEFINITE_WAIT)
+                cMsWait = 1000;
+            else
+            {
+                uint64_t cMsElapsed = RTTimeMilliTS() - u64Started;
+                if (cMsElapsed >= uTimeoutMS)
+                    break; /* timed out */
+                cMsWait = RT_MIN(1000, uTimeoutMS - (uint32_t)cMsElapsed);
+            }
+
+            ComPtr<IEvent> pEvent;
+            hr = mEventSource->GetEvent(pListener, cMsWait, pEvent.asOutParam());
+            if (   SUCCEEDED(hr)
+                && !pEvent.isNull())
+            {
+                VBoxEventType_T aType;
+                hr = pEvent->COMGETTER(Type)(&aType);
+                ComAssertComRC(hr);
+                switch (aType)
+                {
+                    case VBoxEventType_OnGuestSessionStateChanged:
+                    {
+                        ComPtr<IGuestSessionStateChangedEvent> pChangedEvent = pEvent;
+                        Assert(!pChangedEvent.isNull());
+
+                        ULONG uSessionID;
+                        pChangedEvent->COMGETTER(Id)(&uSessionID);
+                        if (uSessionID != mData.mSession.mID)
+                            continue; /* Only our own session is of interest. */
+
+                        GuestSessionStatus_T sessionStatus;
+                        pChangedEvent->COMGETTER(Status)(&sessionStatus);
+                        if (pSessionStatus)
+                            *pSessionStatus = sessionStatus;
+
+                        LogFlowThisFunc(("Got status changed event for session ID=%RU32: %ld\n",
+                                         mData.mSession.mID, sessionStatus));
+
+                        bool fSignal = false;
+                        if (fWaitFlags)
+                        {
+                            switch (sessionStatus)
+                            {
+                                case GuestSessionStatus_Started:
+                                    fSignal = (   fWaitFlags & GuestSessionWaitForFlag_Start
+                                               || fWaitFlags & GuestSessionWaitForFlag_Status);
+                                    break;
+
+                                default:
+                                    fSignal = true;
+                                    break;
+                            }
+                        }
+                        else
+                            fSignal = true;
+
+                        if (!fSignal)
+                            continue;
+
+                        ComPtr<IGuestErrorInfo> errorInfo;
+                        hr = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam());
+                        ComAssertComRC(hr);
+
+                        LONG lGuestRc;
+                        hr = errorInfo->COMGETTER(Result)(&lGuestRc);
+                        ComAssertComRC(hr);
+                        if (RT_FAILURE((int)lGuestRc))
+                            vrc = VERR_GSTCTL_GUEST_ERROR;
+                        if (pGuestRc)
+                            *pGuestRc = (int)lGuestRc;
+
+                        LogFlowThisFunc(("Status changed event for session ID=%RU32: %ld (%Rrc)\n",
+                                         mData.mSession.mID, sessionStatus,
+                                         RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc));
+
+                        fSignalled = true;
+                        break;
+                    }
+
+                    default:
+                         AssertMsgFailed(("Unhandled event type %ld\n", aType));
+                         break;
+                }
+            }
+
+        } while (!fSignalled);
+
+        if (   RT_SUCCESS(vrc)
+            && !fSignalled)
+        {
+            vrc = VERR_TIMEOUT;
+        }
+
+        mEventSource->UnregisterListener(pListener);
+    }
+    else
+        vrc = VERR_COM_UNEXPECTED;
 
     LogFlowFuncLeaveRC(vrc);
@@ -1810,5 +1980,5 @@
 
     /* Remove ourselves from the session list. */
-    int rc2 = mData.mParent->sessionRemove(this);
+    int rc2 = mParent->sessionRemove(this);
     if (RT_SUCCESS(rc))
         rc = rc2;
@@ -1828,5 +1998,5 @@
 
         return setError(VBOX_E_IPRT_ERROR,
-                        tr("Closing guest session failed with %Rrc\n"), rc);
+                        tr("Closing guest session failed with %Rrc"), rc);
     }
 
@@ -2466,4 +2636,8 @@
     if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
+    HRESULT hr = isReadyExternal();
+    if (FAILED(hr))
+        return hr;
+
     /** @todo Validate open mode. */
     /** @todo Validate disposition mode. */
@@ -2471,6 +2645,4 @@
     /** @todo Validate creation mode. */
     uint32_t uCreationMode = 0;
-
-    HRESULT hr = S_OK;
 
     GuestFileOpenInfo openInfo;
@@ -2660,4 +2832,8 @@
     AutoCaller autoCaller(this);
     if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    HRESULT hr = isReadyExternal();
+    if (FAILED(hr))
+        return hr;
 
     GuestProcessStartupInfo procInfo;
@@ -2688,6 +2864,4 @@
     }
 
-    HRESULT hr = S_OK;
-
     if (RT_SUCCESS(rc))
     {
@@ -2732,5 +2906,5 @@
         {
             case VERR_MAX_PROCS_REACHED:
-                hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of guest objects per session (%ld) reached"),
+                hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of concurrent guest processes per session (%ld) reached"),
                                                     VBOX_GUESTCTRL_MAX_OBJECTS);
                 break;
Index: /trunk/src/VBox/Main/testcase/tstGuestCtrlContextID.cpp
===================================================================
--- /trunk/src/VBox/Main/testcase/tstGuestCtrlContextID.cpp	(revision 45414)
+++ /trunk/src/VBox/Main/testcase/tstGuestCtrlContextID.cpp	(revision 45415)
@@ -35,5 +35,5 @@
 {
     RTTEST hTest;
-    int rc = RTTestInitAndCreate("tstMakeup", &hTest);
+    int rc = RTTestInitAndCreate("tstGuestCtrlContextID", &hTest);
     if (rc)
         return rc;
@@ -76,6 +76,6 @@
     RTTestIPrintf(RTTESTLVL_DEBUG, "Max context is: %RU32\n", uContextMax);
 
-    /* Do 64 tests total. */
-    for (int t = 0; t < 64 && !RTTestErrorCount(hTest); t++)
+    /* Do 4048 tests total. */
+    for (int t = 0; t < 4048 && !RTTestErrorCount(hTest); t++)
     {
         /* VBOX_GUESTCTRL_MAX_* includes 0 as an object, so subtract one. */
