Index: /trunk/include/VBox/HostServices/GuestControlSvc.h
===================================================================
--- /trunk/include/VBox/HostServices/GuestControlSvc.h	(revision 42460)
+++ /trunk/include/VBox/HostServices/GuestControlSvc.h	(revision 42461)
@@ -44,5 +44,4 @@
 /**
  * Process status when executed in the guest.
- * Note: Has to match Main's ExecuteProcessStatus_*!
  */
 enum eProcessStatus
@@ -250,5 +249,15 @@
      * new data on stdout/stderr, process terminated etc.
      */
-    HOST_EXEC_GET_OUTPUT = 102
+    HOST_EXEC_GET_OUTPUT = 102,
+
+    /*
+     * Guest control 2.0 commands start in the 2xx number space.
+     */
+
+    /**
+     * Waits for a certain event to happen. This can be an input, output
+     * or status event.
+     */
+    HOST_EXEC_WAIT_FOR = 210
 };
 
@@ -278,4 +287,6 @@
     /*
      * Process execution.
+     * The 1xx commands are legacy guest control commands and
+     * will be replaced by newer commands in the future.
      */
 
@@ -291,5 +302,17 @@
      * Guests sends an input status notification to the host.
      */
-    GUEST_EXEC_SEND_INPUT_STATUS = 102
+    GUEST_EXEC_SEND_INPUT_STATUS = 102,
+
+    /*
+     * Guest control 2.0 commands start in the 2xx number space.
+     */
+
+    /**
+     * Guest notifies the host about some I/O event. This can be
+     * a stdout, stderr or a stdin event. The actual event only tells
+     * how many data is available / can be sent without actually
+     * transmitting the data.
+     */
+    GUEST_EXEC_IO_NOTIFY = 210,
 };
 
@@ -332,5 +355,5 @@
     /** The command to execute on the guest. */
     HGCMFunctionParameter cmd;
-    /** Execution flags (see IGuest::ExecuteProcessFlag_*). */
+    /** Execution flags (see IGuest::ProcessCreateFlag_*). */
     HGCMFunctionParameter flags;
     /** Number of arguments. */
@@ -351,5 +374,5 @@
      *  overall lifetime of the process or how long it
      *  can take to bring the process up and running -
-     *  (depends on the IGuest::ExecuteProcessFlag_*). */
+     *  (depends on the IGuest::ProcessCreateFlag_*). */
     HGCMFunctionParameter timeout;
 
@@ -434,4 +457,17 @@
 } VBoxGuestCtrlHGCMMsgExecStatusIn;
 
+/**
+ * Reports back the currente I/O status of a guest process.
+ */
+typedef struct VBoxGuestCtrlHGCMMsgExecIONotify
+{
+    VBoxGuestHGCMCallInfo hdr;
+    /** Context ID. */
+    HGCMFunctionParameter context;
+    /** Data written. */
+    HGCMFunctionParameter written;
+
+} VBoxGuestCtrlHGCMMsgExecIONotify;
+
 #pragma pack ()
 
Index: /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlThread.cpp
===================================================================
--- /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlThread.cpp	(revision 42460)
+++ /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlThread.cpp	(revision 42461)
@@ -486,5 +486,5 @@
             }
 
-            /* Reqport back actual data written (if any). */
+            /* Report back actual data written (if any). */
             pRequest->cbData = cbWritten;
             break;
Index: /trunk/src/VBox/Main/idl/VirtualBox.xidl
===================================================================
--- /trunk/src/VBox/Main/idl/VirtualBox.xidl	(revision 42460)
+++ /trunk/src/VBox/Main/idl/VirtualBox.xidl	(revision 42461)
@@ -8884,5 +8884,5 @@
   <enum
     name="ProcessWaitResult"
-    uuid="c593a234-21ee-4a67-85ef-ba0cc9b9b86a"
+    uuid="24a4d49f-a7c1-44b0-b01f-5686a316466b"
     >
     <desc>
@@ -8915,4 +8915,7 @@
     </const>    
     <const name="StdErr"                  value="8">
+      <desc>TODO</desc>
+    </const>
+    <const name="Any"                     value="9">
       <desc>TODO</desc>
     </const>    
Index: /trunk/src/VBox/Main/include/GuestCtrlImplPrivate.h
===================================================================
--- /trunk/src/VBox/Main/include/GuestCtrlImplPrivate.h	(revision 42460)
+++ /trunk/src/VBox/Main/include/GuestCtrlImplPrivate.h	(revision 42461)
@@ -127,5 +127,5 @@
     void Destroy(void);
 
-    int FillData(const void *pData, size_t cbData);
+    int FillData(const void *pvToWrite, size_t cbToWrite);
 
     int Init(eVBoxGuestCtrlCallbackType enmType);
@@ -133,7 +133,11 @@
     eVBoxGuestCtrlCallbackType GetCallbackType(void) { return mType; }
 
-protected:
-
-    /** Pointer to user-supplied data. */
+    const void* GetPayloadRaw(void) const { return pvPayload; }
+
+    size_t GetPayloadSize(void) { return cbPayload; }
+
+protected:
+
+    /** Pointer to actual callback data. */
     void                       *pvData;
     /** Size of user-supplied data. */
@@ -143,4 +147,9 @@
     /** Callback flags. */
     uint32_t                    uFlags;
+    /** Payload which will be available on successful
+     *  waiting (optional). */
+    void                       *pvPayload;
+    /** Size of the payload. */
+    size_t                      cbPayload;
 };
 typedef std::map < uint32_t, GuestCtrlCallback* > GuestCtrlCallbacks;
Index: /trunk/src/VBox/Main/include/GuestImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/GuestImpl.h	(revision 42460)
+++ /trunk/src/VBox/Main/include/GuestImpl.h	(revision 42461)
@@ -207,4 +207,5 @@
      * @{ */
     int         dispatchToSession(uint32_t uContextID, uint32_t uFunction, void *pvData, size_t cbData);
+    uint32_t    getAdditionsVersion(void) { return mData.mAdditionsVersionFull; }
     Console    *getConsole(void) { return mParent; }
     int         sessionClose(ComObjPtr<GuestSession> pSession);
Index: /trunk/src/VBox/Main/include/GuestProcessImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/GuestProcessImpl.h	(revision 42460)
+++ /trunk/src/VBox/Main/include/GuestProcessImpl.h	(revision 42461)
@@ -76,5 +76,5 @@
     bool isReady(void);
     ULONG getPID(void) { return mData.mPID; }
-    int readData(ULONG uHandle, ULONG uSize, ULONG uTimeoutMS, BYTE *pbData, size_t cbData);
+    int readData(ULONG uHandle, ULONG uSize, ULONG uTimeoutMS, BYTE *pbData, size_t cbData, size_t *pcbRead);
     int startProcess(void);
     int startProcessAsync(void);
@@ -92,4 +92,5 @@
     int onGuestDisconnected(GuestCtrlCallback *pCallback, PCALLBACKDATACLIENTDISCONNECTED pData);
     int onProcessInputStatus(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECINSTATUS pData);
+    int onProcessNotifyIO(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECSTATUS pData);
     int onProcessStatusChange(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECSTATUS pData);
     int onProcessOutput(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECOUT pData);
Index: /trunk/src/VBox/Main/include/GuestSessionImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/GuestSessionImpl.h	(revision 42460)
+++ /trunk/src/VBox/Main/include/GuestSessionImpl.h	(revision 42461)
@@ -128,8 +128,10 @@
     const GuestCredentials &getCredentials(void);
     const GuestEnvironment &getEnvironment(void);
+    uint32_t                getProtocolVersion(void) { return mData.mProtocolVersion; }
     int                     processClose(ComObjPtr<GuestProcess> pProcess);
     int                     processCreateExInteral(GuestProcessInfo &procInfo, ComObjPtr<GuestProcess> &pProgress);
     inline bool             processExists(uint32_t uProcessID, ComObjPtr<GuestProcess> *pProcess);
     inline int              processGetByPID(ULONG uPID, ComObjPtr<GuestProcess> *pProcess);
+    int                     queryInfo(void);
     /** @}  */
 
@@ -138,4 +140,8 @@
     struct Data
     {
+        /** Guest control protocol version.
+         *  Guest control prior to VBox 4.2 has version 1,
+         *  guest control 2.0 has ...well, 2. */
+        uint32_t             mProtocolVersion;
         /** Flag indicating if this is an internal session
          *  or not. Internal session are not accessible by clients. */
Index: /trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp	(revision 42460)
+++ /trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp	(revision 42461)
@@ -2816,4 +2816,7 @@
         if (FAILED(hr2))
             rc = VERR_COM_OBJECT_NOT_FOUND;
+
+        if (RT_SUCCESS(rc))
+            rc = pSession->queryInfo();
     }
 
Index: /trunk/src/VBox/Main/src-client/GuestCtrlPrivate.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/GuestCtrlPrivate.cpp	(revision 42460)
+++ /trunk/src/VBox/Main/src-client/GuestCtrlPrivate.cpp	(revision 42461)
@@ -122,6 +122,8 @@
     : pvData(NULL),
       cbData(0),
-     mType(VBOXGUESTCTRLCALLBACKTYPE_UNKNOWN),
-      uFlags(0)
+      mType(VBOXGUESTCTRLCALLBACKTYPE_UNKNOWN),
+      uFlags(0),
+      pvPayload(NULL),
+      cbPayload(0)
 {
 }
@@ -131,5 +133,7 @@
       cbData(0),
       mType(VBOXGUESTCTRLCALLBACKTYPE_UNKNOWN),
-      uFlags(0)
+      uFlags(0),
+      pvPayload(NULL),
+      cbPayload(0)
 {
     int rc = Init(enmType);
@@ -206,18 +210,26 @@
     }
     cbData = 0;
-}
-
-int GuestCtrlCallback::FillData(const void *pData, size_t cbData)
-{
-    if (!cbData)
+
+    if (pvPayload)
+    {
+        RTMemFree(pvPayload);
+        pvPayload = NULL;
+    }
+    cbPayload = 0;
+}
+
+int GuestCtrlCallback::FillData(const void *pvToWrite, size_t cbToWrite)
+{
+    if (!cbToWrite)
         return VINF_SUCCESS;
-    AssertPtr(pData);
-
-    Assert(pvData == NULL); /* Can't reuse callbacks! */
-    pvData = RTMemAlloc(cbData);
-    if (!pvData)
+    AssertPtr(pvToWrite);
+
+    Assert(pvPayload == NULL); /* Can't reuse callbacks! */
+    pvPayload = RTMemAlloc(cbToWrite);
+    if (!pvPayload)
         return VERR_NO_MEMORY;
 
-    memcpy(pvData, pData, cbData);
+    memcpy(pvPayload, pvToWrite, cbToWrite);
+    cbPayload = cbToWrite;
 
     return VINF_SUCCESS;
Index: /trunk/src/VBox/Main/src-client/GuestProcessImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/GuestProcessImpl.cpp	(revision 42460)
+++ /trunk/src/VBox/Main/src-client/GuestProcessImpl.cpp	(revision 42461)
@@ -82,5 +82,4 @@
 
     HRESULT hr = BaseFinalConstruct();
-    LogFlowFuncLeaveRC(hr);
     return hr;
 }
@@ -308,16 +307,4 @@
 // private methods
 /////////////////////////////////////////////////////////////////////////////
-
-/*
-
-    SYNC TO ASK:
-    Everything which involves HGCM communication (start, read/write/status(?)/...)
-    either can be called synchronously or asynchronously by running in a Main worker
-    thread.
-
-    Rules:
-        - Only one async operation per process a time can be around.
-
-*/
 
 inline int GuestProcess::callbackAdd(GuestCtrlCallback *pCallback, ULONG *puContextID)
@@ -387,72 +374,72 @@
     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
-#ifdef DEBUG
-    LogFlowFunc(("Callback count=%RU32\n", VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID)));
-#endif
-
     int rc;
+
+    /* 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(uContextID));
     if (it != mData.mCallbacks.end())
     {
-        GuestCtrlCallback *pCallback = it->second;
+        pCallback = it->second;
         AssertPtr(pCallback);
-
-        switch (uFunction)
-        {
-            case GUEST_DISCONNECTED:
-            {
-                PCALLBACKDATACLIENTDISCONNECTED pCallbackData = reinterpret_cast<PCALLBACKDATACLIENTDISCONNECTED>(pvData);
-                AssertPtr(pCallbackData);
-                AssertReturn(sizeof(CALLBACKDATACLIENTDISCONNECTED) == cbData, VERR_INVALID_PARAMETER);
-                AssertReturn(CALLBACKDATAMAGIC_CLIENT_DISCONNECTED == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
-
-                rc = onGuestDisconnected(pCallback, pCallbackData); /* Affects all callbacks. */
-                break;
-            }
-
-            case GUEST_EXEC_SEND_STATUS:
-            {
-                PCALLBACKDATAEXECSTATUS pCallbackData = reinterpret_cast<PCALLBACKDATAEXECSTATUS>(pvData);
-                AssertPtr(pCallbackData);
-                AssertReturn(sizeof(CALLBACKDATAEXECSTATUS) == cbData, VERR_INVALID_PARAMETER);
-                AssertReturn(CALLBACKDATAMAGIC_EXEC_STATUS == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
-
-                rc = onProcessStatusChange(pCallback, pCallbackData);
-                break;
-            }
-
-            case GUEST_EXEC_SEND_OUTPUT:
-            {
-                PCALLBACKDATAEXECOUT pCallbackData = reinterpret_cast<PCALLBACKDATAEXECOUT>(pvData);
-                AssertPtr(pCallbackData);
-                AssertReturn(sizeof(CALLBACKDATAEXECOUT) == cbData, VERR_INVALID_PARAMETER);
-                AssertReturn(CALLBACKDATAMAGIC_EXEC_OUT == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
-
-                Assert(mData.mPID == pCallbackData->u32PID);
-                rc = onProcessOutput(pCallback, pCallbackData);
-                break;
-            }
-
-            case GUEST_EXEC_SEND_INPUT_STATUS:
-            {
-                PCALLBACKDATAEXECINSTATUS pCallbackData = reinterpret_cast<PCALLBACKDATAEXECINSTATUS>(pvData);
-                AssertPtr(pCallbackData);
-                AssertReturn(sizeof(CALLBACKDATAEXECINSTATUS) == cbData, VERR_INVALID_PARAMETER);
-                AssertReturn(CALLBACKDATAMAGIC_EXEC_IN_STATUS == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
-
-                Assert(mData.mPID == pCallbackData->u32PID);
-                rc = onProcessInputStatus(pCallback, pCallbackData);
-                break;
-            }
-
-            default:
-                /* Silently ignore not implemented functions. */
-                rc = VERR_NOT_IMPLEMENTED;
-                break;
-        }
-    }
-    else
-        rc = VERR_NOT_FOUND;
+#ifdef DEBUG
+        LogFlowFunc(("pCallback=%p\n", pCallback));
+#endif
+    }
+
+    switch (uFunction)
+    {
+        case GUEST_DISCONNECTED:
+        {
+            PCALLBACKDATACLIENTDISCONNECTED pCallbackData = reinterpret_cast<PCALLBACKDATACLIENTDISCONNECTED>(pvData);
+            AssertPtr(pCallbackData);
+            AssertReturn(sizeof(CALLBACKDATACLIENTDISCONNECTED) == cbData, VERR_INVALID_PARAMETER);
+            AssertReturn(CALLBACKDATAMAGIC_CLIENT_DISCONNECTED == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
+
+            rc = onGuestDisconnected(pCallback, pCallbackData); /* Affects all callbacks. */
+            break;
+        }
+
+        case GUEST_EXEC_SEND_STATUS:
+        {
+            PCALLBACKDATAEXECSTATUS pCallbackData = reinterpret_cast<PCALLBACKDATAEXECSTATUS>(pvData);
+            AssertPtr(pCallbackData);
+            AssertReturn(sizeof(CALLBACKDATAEXECSTATUS) == cbData, VERR_INVALID_PARAMETER);
+            AssertReturn(CALLBACKDATAMAGIC_EXEC_STATUS == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
+
+            rc = onProcessStatusChange(pCallback, pCallbackData);
+            break;
+        }
+
+        case GUEST_EXEC_SEND_OUTPUT:
+        {
+            PCALLBACKDATAEXECOUT pCallbackData = reinterpret_cast<PCALLBACKDATAEXECOUT>(pvData);
+            AssertPtr(pCallbackData);
+            AssertReturn(sizeof(CALLBACKDATAEXECOUT) == cbData, VERR_INVALID_PARAMETER);
+            AssertReturn(CALLBACKDATAMAGIC_EXEC_OUT == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
+
+            rc = onProcessOutput(pCallback, pCallbackData);
+            break;
+        }
+
+        case GUEST_EXEC_SEND_INPUT_STATUS:
+        {
+            PCALLBACKDATAEXECINSTATUS pCallbackData = reinterpret_cast<PCALLBACKDATAEXECINSTATUS>(pvData);
+            AssertPtr(pCallbackData);
+            AssertReturn(sizeof(CALLBACKDATAEXECINSTATUS) == cbData, VERR_INVALID_PARAMETER);
+            AssertReturn(CALLBACKDATAMAGIC_EXEC_IN_STATUS == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
+
+            rc = onProcessInputStatus(pCallback, pCallbackData);
+            break;
+        }
+
+        default:
+            /* Silently ignore not implemented functions. */
+            rc = VERR_NOT_IMPLEMENTED;
+            break;
+    }
 
 #ifdef DEBUG
@@ -473,5 +460,5 @@
     GuestCtrlCallbacks::iterator it =
         mData.mCallbacks.find(VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID));
-    if (it == mData.mCallbacks.end())
+    if (it != mData.mCallbacks.end())
     {
         delete it->second;
@@ -537,5 +524,5 @@
 int GuestProcess::onGuestDisconnected(GuestCtrlCallback *pCallback, PCALLBACKDATACLIENTDISCONNECTED pData)
 {
-    AssertPtrReturn(pCallback, VERR_INVALID_POINTER);
+    /* pCallback is optional. */
     AssertPtrReturn(pData, VERR_INVALID_POINTER);
 
@@ -545,5 +532,6 @@
 
     /* First, signal callback in every case. */
-    pCallback->Signal();
+    if (pCallback)
+        pCallback->Signal();
 
     /* Do we need to report a termination? */
@@ -564,5 +552,5 @@
 int GuestProcess::onProcessInputStatus(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECINSTATUS pData)
 {
-    AssertPtrReturn(pCallback, VERR_INVALID_POINTER);
+    /* pCallback is optional. */
     AssertPtrReturn(pData, VERR_INVALID_POINTER);
 
@@ -575,5 +563,6 @@
 
     /* First, signal callback in every case. */
-    pCallback->Signal();
+    if (pCallback)
+        pCallback->Signal();
 
     /* Then do the WaitFor signalling stuff. */
@@ -588,7 +577,15 @@
 }
 
+int GuestProcess::onProcessNotifyIO(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECSTATUS pData)
+{
+    /* pCallback is optional. */
+    AssertPtrReturn(pData, VERR_INVALID_POINTER);
+
+    return 0;
+}
+
 int GuestProcess::onProcessStatusChange(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECSTATUS pData)
 {
-    AssertPtrReturn(pCallback, VERR_INVALID_POINTER);
+    /* pCallback is optional. */
     AssertPtrReturn(pData, VERR_INVALID_POINTER);
 
@@ -620,5 +617,5 @@
         case PROC_STS_TEN:
         {
-            fSignal = (uWaitFlags & ProcessWaitForFlag_Terminate);
+            fSignal = TRUE; /* Signal in any case. */
             waitRes = ProcessWaitResult_Status;
 
@@ -630,5 +627,5 @@
         case PROC_STS_TES:
         {
-            fSignal = (uWaitFlags & ProcessWaitForFlag_Terminate);
+            fSignal = TRUE; /* Signal in any case. */
             waitRes = ProcessWaitResult_Status;
 
@@ -640,5 +637,5 @@
         case PROC_STS_TEA:
         {
-            fSignal = (uWaitFlags & ProcessWaitForFlag_Terminate);
+            fSignal = TRUE; /* Signal in any case. */
             waitRes = ProcessWaitResult_Status;
 
@@ -649,5 +646,5 @@
         case PROC_STS_TOK:
         {
-            fSignal = (uWaitFlags & ProcessWaitForFlag_Terminate);
+            fSignal = TRUE; /* Signal in any case. */
             waitRes = ProcessWaitResult_Timeout;
 
@@ -658,5 +655,5 @@
         case PROC_STS_TOA:
         {
-            fSignal = (uWaitFlags & ProcessWaitForFlag_Terminate);
+            fSignal = TRUE; /* Signal in any case. */
             waitRes = ProcessWaitResult_Timeout;
 
@@ -667,5 +664,5 @@
         case PROC_STS_DWN:
         {
-            fSignal = (uWaitFlags & ProcessWaitForFlag_Terminate);
+            fSignal = TRUE; /* Signal in any case. */
             waitRes = (mData.mProcess.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
                     ? ProcessWaitResult_Terminate : ProcessWaitResult_Status;
@@ -759,5 +756,6 @@
      * Now do the signalling stuff.
      */
-    rc = pCallback->Signal();
+    if (pCallback)
+        rc = pCallback->Signal();
 
     if (fSignal)
@@ -774,5 +772,5 @@
 int GuestProcess::onProcessOutput(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECOUT pData)
 {
-    AssertPtrReturn(pCallback, VERR_INVALID_POINTER);
+    /* pCallback is optional. */
     AssertPtrReturn(pData, VERR_INVALID_POINTER);
 
@@ -780,11 +778,17 @@
                  mData.mPID, pData->u32HandleId, pData->u32Flags, pData->pvData, pData->cbData, pCallback, pData));
 
-    /* Copy data into callback. */
-    int rc = pCallback->FillData(pData->pvData, pData->cbData);
-
-    /* First, signal callback in every case. */
-    int rc2 = pCallback->Signal();
-    if (RT_SUCCESS(rc))
-        rc = rc2;
+    /* Copy data into callback (if any). */
+    int rc = VINF_SUCCESS;
+
+    /* First, signal callback in every case (if available). */
+    if (pCallback)
+    {
+        if (pData->pvData && pData->cbData)
+            rc = pCallback->FillData(pData->pvData, pData->cbData);
+
+        int rc2 = pCallback->Signal();
+        if (RT_SUCCESS(rc))
+            rc = rc2;
+    }
 
     /* Then do the WaitFor signalling stuff. */
@@ -811,10 +815,9 @@
     if (fSignal)
     {
-        rc2 = signalWaiters(  pData->u32HandleId == OUTPUT_HANDLE_ID_STDOUT
-                            ? ProcessWaitResult_StdOut : ProcessWaitResult_StdErr);
+        int rc2 = signalWaiters(  pData->u32HandleId == OUTPUT_HANDLE_ID_STDOUT
+                                ? ProcessWaitResult_StdOut : ProcessWaitResult_StdErr);
         if (RT_SUCCESS(rc))
             rc = rc2;
     }
-    AssertRC(rc);
 
     LogFlowFuncLeaveRC(rc);
@@ -822,14 +825,83 @@
 }
 
-int GuestProcess::readData(ULONG uHandle, ULONG uSize, ULONG uTimeoutMS, BYTE *pbData, size_t cbData)
-{
-    LogFlowFunc(("uPID=%RU32, uHandle=%RU32, uSize=%RU32, uTimeoutMS=%RU32, pbData=%p, cbData=%z\n",
+int GuestProcess::readData(ULONG uHandle, ULONG uSize, ULONG uTimeoutMS,
+                           BYTE *pbData, size_t cbData, size_t *pcbRead)
+{
+    LogFlowFunc(("uPID=%RU32, uHandle=%RU32, uSize=%RU32, uTimeoutMS=%RU32, pbData=%p, cbData=%RU32\n",
                  mData.mPID, uHandle, uSize, uTimeoutMS, pbData, cbData));
     AssertReturn(uSize, VERR_INVALID_PARAMETER);
     AssertPtrReturn(pbData, VERR_INVALID_POINTER);
     AssertReturn(cbData >= uSize, VERR_INVALID_PARAMETER);
-
-    LogFlowFuncLeave();
-    return 0;
+    /* pcbRead is optional. */
+
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    if (mData.mStatus != ProcessStatus_Started)
+        return VERR_NOT_AVAILABLE;
+
+    ULONG uContextID = 0;
+    GuestCtrlCallback *pCallbackRead = new GuestCtrlCallback();
+    if (!pCallbackRead)
+        return VERR_NO_MEMORY;
+
+    /* Create callback and add it to the map. */
+    int rc = pCallbackRead->Init(VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT);
+    if (RT_SUCCESS(rc))
+        rc = callbackAdd(pCallbackRead, &uContextID);
+
+    alock.release(); /* Drop the write lock again. */
+
+    if (RT_SUCCESS(rc))
+    {
+        VBOXHGCMSVCPARM paParms[5];
+
+        int i = 0;
+        paParms[i++].setUInt32(uContextID);
+        paParms[i++].setUInt32(mData.mPID);
+        paParms[i++].setUInt32(uHandle);
+        paParms[i++].setUInt32(0 /* Flags, none set yet. */);
+
+        rc = sendCommand(HOST_EXEC_GET_OUTPUT, i, paParms);
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Let's wait for the process being started.
+         * Note: Be sure not keeping a AutoRead/WriteLock here.
+         */
+        LogFlowFunc(("Waiting for callback (%RU32ms) ...\n", uTimeoutMS));
+        rc = pCallbackRead->Wait(uTimeoutMS);
+        if (RT_SUCCESS(rc)) /* Wait was successful, check for supplied information. */
+        {
+            rc = pCallbackRead->GetResultCode();
+            LogFlowFunc(("Callback returned rc=%Rrc, cbData=%RU32\n", rc, pCallbackRead->GetPayloadSize()));
+
+            if (RT_SUCCESS(rc))
+            {
+                size_t cbDataCB = pCallbackRead->GetPayloadSize();
+                if (cbDataCB)
+                {
+                    Assert(cbData >= cbDataCB);
+                    memcpy(pbData, pCallbackRead->GetPayloadRaw(), cbDataCB);
+                }
+
+                if (pcbRead)
+                    *pcbRead = cbDataCB;
+            }
+        }
+        else
+            rc = VERR_TIMEOUT;
+    }
+
+    alock.acquire();
+
+    AssertPtr(pCallbackRead);
+    int rc2 = callbackRemove(uContextID);
+    if (RT_SUCCESS(rc))
+        rc = rc2;
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
 }
 
@@ -839,6 +911,6 @@
     LogFlowThisFuncEnter();
 
-    Console *pConsole = mData.mConsole;
-    AssertPtr(pConsole);
+    ComObjPtr<Console> pConsole = mData.mConsole;
+    Assert(!pConsole.isNull());
 
     /* Forward the information to the VMM device. */
@@ -850,4 +922,14 @@
     if (RT_FAILURE(rc))
     {
+        int rc2;
+        if (rc == VERR_INVALID_VM_HANDLE)
+            rc2 = setErrorInternal(rc, tr("VMM device is not available (is the VM running?)"));
+        else if (rc == VERR_NOT_FOUND)
+            rc2 = setErrorInternal(rc, tr("The guest execution service is not ready (yet)"));
+        else if (rc == VERR_HGCM_SERVICE_NOT_FOUND)
+            rc2 = setErrorInternal(rc, tr("The guest execution service is not available"));
+        else
+            rc2 = setErrorInternal(rc, Utf8StrFmt(tr("The HGCM call failed with error %Rrc"), rc));
+        AssertRC(rc2);
     }
 
@@ -983,17 +1065,4 @@
 
             rc = sendCommand(HOST_EXEC_CMD, i, paParms);
-            if (RT_FAILURE(rc))
-            {
-                int rc2;
-                if (rc == VERR_INVALID_VM_HANDLE)
-                    rc2 = setErrorInternal(rc, tr("VMM device is not available (is the VM running?)"));
-                else if (rc == VERR_NOT_FOUND)
-                    rc2 = setErrorInternal(rc, tr("The guest execution service is not ready (yet)"));
-                else if (rc == VERR_HGCM_SERVICE_NOT_FOUND)
-                    rc2 = setErrorInternal(rc, tr("The guest execution service is not available"));
-                else
-                    rc2 = setErrorInternal(rc, Utf8StrFmt(tr("The HGCM call failed with error %Rrc"), rc));
-                AssertRC(rc2);
-            }
         }
 
@@ -1004,5 +1073,5 @@
         uint32_t uTimeoutMS = mData.mProcess.mTimeoutMS;
 
-        alock.release(); /* Drop the read lock again. */
+        alock.release(); /* Drop the write lock again. */
 
         if (RT_SUCCESS(rc))
@@ -1085,4 +1154,7 @@
     LogFlowThisFuncEnter();
 
+    if (mData.mParent->getProtocolVersion() < 2)
+        return VERR_NOT_SUPPORTED;
+
     LogFlowFuncLeave();
     return VERR_NOT_IMPLEMENTED;
@@ -1095,10 +1167,12 @@
     AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
 
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
     LogFlowFunc(("fWaitFlags=%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p\n",
                  fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent));
 
-    ProcessStatus_T curStatus = mData.mStatus;
+    ProcessStatus_T curStatus;
+    {
+        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+        curStatus = mData.mStatus;
+    }
 
     /* Did some error occur before? Then skip waiting and return. */
@@ -1117,4 +1191,20 @@
         || (fWaitFlags & ProcessWaitForFlag_StdErr))
     {
+        /* Filter out waits which are *not* supported using
+         * older guest control Guest Additions. */
+        AssertPtr(mData.mParent);
+        if (mData.mParent->getProtocolVersion() < 2)
+        {
+            /* We don't support waiting for stdin, out + err,
+             * just skip waiting then. */
+            if (   (fWaitFlags & ProcessWaitForFlag_StdIn)
+                || (fWaitFlags & ProcessWaitForFlag_StdOut)
+                || (fWaitFlags & ProcessWaitForFlag_StdErr))
+            {
+                /* Use _Any because we don't know what to tell the caller. */
+                waitRes.mResult = ProcessWaitResult_Any;
+            }
+        }
+
         switch (mData.mStatus)
         {
@@ -1138,4 +1228,5 @@
             case ProcessStatus_Undefined:
             case ProcessStatus_Starting:
+            case ProcessStatus_Started:
                 /* Do the waiting below. */
                 break;
@@ -1181,4 +1272,6 @@
     if (waitRes.mResult != ProcessWaitResult_None)
         return VINF_SUCCESS;
+
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
     if (mData.mWaitCount > 0)
@@ -1348,5 +1441,5 @@
 int GuestProcess::writeData(ULONG uHandle, const BYTE *pbData, size_t cbData, ULONG uTimeoutMS, ULONG *puWritten)
 {
-    LogFlowFunc(("uPID=%RU32, uHandle=%RU32, pbData=%p, cbData=%z, uTimeoutMS=%RU32, puWritten=%p\n",
+    LogFlowFunc(("uPID=%RU32, uHandle=%RU32, pbData=%p, cbData=%RU32, uTimeoutMS=%RU32, puWritten=%p\n",
                  mData.mPID, uHandle, pbData, cbData, uTimeoutMS, puWritten));
     AssertPtrReturn(pbData, VERR_INVALID_POINTER);
@@ -1366,8 +1459,8 @@
     ReturnComNotImplemented();
 #else
-    LogFlowThisFuncEnter();
-
+    if (aSize < 0)
+        return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
     if (aSize == 0)
-        return setError(E_INVALIDARG, tr("Invalid size to read specified"));
+        return setError(E_INVALIDARG, tr("The size (%lld) is zero"), aSize);
     CheckComArgOutSafeArrayPointerValid(aData);
 
@@ -1375,14 +1468,19 @@
     if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    com::SafeArray<BYTE> data(aSize);
-    int rc = readData(aHandle, aSize, aTimeoutMS, data.raw(), aSize);
+    com::SafeArray<BYTE> data((size_t)aSize);
+    Assert(data.size() >= aSize);
+
+    size_t cbRead;
+    int rc = readData(aHandle, aSize, aTimeoutMS, data.raw(), aSize, &cbRead);
     if (RT_SUCCESS(rc))
+    {
+        if (data.size() != cbRead)
+            data.resize(cbRead);
         data.detachTo(ComSafeArrayOutArg(aData));
+    }
 
     /** @todo Do setError() here. */
     HRESULT hr = RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
-    LogFlowFuncLeaveRC(hr);
+    LogFlowFuncLeaveRC(rc);
 
     return hr;
@@ -1405,5 +1503,5 @@
     /** @todo Do setError() here. */
     HRESULT hr = RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
-    LogFlowFuncLeaveRC(hr);
+    LogFlowFuncLeaveRC(rc);
 
     return hr;
@@ -1471,8 +1569,5 @@
         fWaitFor |= flags[i];
 
-    HRESULT hr = WaitFor(fWaitFor, aTimeoutMS, aReason);
-
-    LogFlowFuncLeaveRC(hr);
-    return hr;
+    return WaitFor(fWaitFor, aTimeoutMS, aReason);
 #endif /* VBOX_WITH_GUEST_CONTROL */
 }
@@ -1496,5 +1591,5 @@
     /** @todo Do setError() here. */
     HRESULT hr = RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
-    LogFlowFuncLeaveRC(hr);
+    LogFlowFuncLeaveRC(rc);
 
     return hr;
Index: /trunk/src/VBox/Main/src-client/GuestSessionImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/GuestSessionImpl.cpp	(revision 42460)
+++ /trunk/src/VBox/Main/src-client/GuestSessionImpl.cpp	(revision 42461)
@@ -31,4 +31,5 @@
 
 #include <VBox/com/array.h>
+#include <VBox/version.h>
 
 
@@ -562,4 +563,36 @@
 }
 
+/**
+ * Queries/collects information prior to establishing a guest session.
+ * This is necessary to know which guest control protocol version to use,
+ * among other things (later).
+ *
+ * @return  IPRT status code.
+ */
+int GuestSession::queryInfo(void)
+{
+    /*
+     * Try querying the guest control protocol version running on the guest.
+     * This is done using the Guest Additions version
+     */
+    ComObjPtr<Guest> pGuest = mData.mParent;
+    Assert(!pGuest.isNull());
+
+    uint32_t uVerAdditions = pGuest->getAdditionsVersion();
+    mData.mProtocolVersion = (   VBOX_FULL_VERSION_GET_MAJOR(uVerAdditions) >= 4
+                              && VBOX_FULL_VERSION_GET_MINOR(uVerAdditions) >= 2 )
+                           ? 2  /* Guest control 2.0. */
+                           : 1; /* Legacy guest control (VBox < 4.2). */
+    /* Build revision is ignored. */
+
+    /* Tell the user but don't bitch too often. */
+    static short s_gctrlLegacyWarning = 0;
+    if (s_gctrlLegacyWarning++ < 3) /** @todo Find a bit nicer text. */
+        LogRel((tr("Warning: Guest Additions are older (%ld.%ld) than host capabilities for guest control, please upgrade them. Using protocol version %ld now\n"),
+                VBOX_FULL_VERSION_GET_MAJOR(uVerAdditions), VBOX_FULL_VERSION_GET_MINOR(uVerAdditions), mData.mProtocolVersion));
+
+    return VINF_SUCCESS;
+}
+
 // implementation of public methods
 /////////////////////////////////////////////////////////////////////////////
