Index: /trunk/include/VBox/HostServices/DragAndDropSvc.h
===================================================================
--- /trunk/include/VBox/HostServices/DragAndDropSvc.h	(revision 58328)
+++ /trunk/include/VBox/HostServices/DragAndDropSvc.h	(revision 58329)
@@ -32,24 +32,25 @@
  * Protocol changelog:
  *
- *     Protocol v1 (VBox < 5.0):
- *         - Initial implementation which only implemented host to guest transfers.
- *         - For file transfers all file information such as the file name and file size were
+ *     Protocol v1 (VBox < 5.0, deprecated):
+ *         | Initial implementation which only implemented host to guest transfers.
+ *         | For file transfers all file information such as the file name and file size were
  *           transferred with every file data chunk being sent.
  *
- *     Protocol v2 (VBox 5.0):
- *         - Added support for guest to host transfers.
- *         - Added protocol version support through VBOXDNDCONNECTMSG. The host takes the installed
+ *     Protocol v2 (VBox 5.0 - VBox 5.0.8, deprecated):
+ *         + Added support for guest to host transfers.
+ *         + Added protocol version support through VBOXDNDCONNECTMSG. The host takes the installed
  *           Guest Additions version as indicator which protocol to use for communicating with the guest.
  *           The guest itself uses VBOXDNDCONNECTMSG to report its supported protocol version to the DnD service.
  *
- *     Protocol v3 (VBox 5.0+):
-             Added context IDs for every HGCM message. Not used yet and must be 0.
- *         - Added VBOXDNDSNDDATAHDR and VBOXDNDCBSNDDATAHDRDATA to support (simple) accounting of objects
+ *     Protocol v3 (VBox 5.0.10 and up, current):
+ *         + Added VBOXDNDDISCONNECTMSG for being able to track client disconnects on host side (Main).
+ *         + Added context IDs for every HGCM message. Not used yet and must be 0.
+ *         + Added VBOXDNDSNDDATAHDR and VBOXDNDCBSNDDATAHDRDATA to support (simple) accounting of objects
  *           being transferred, along with supplying separate meta data size (which is part of the total size being sent).
- *         - Added new HOST_DND_HG_SND_DATA_HDR + GUEST_DND_GH_SND_DATA_HDR commands which now allow specifying an optional
+ *         + Added new HOST_DND_HG_SND_DATA_HDR + GUEST_DND_GH_SND_DATA_HDR commands which now allow specifying an optional
  *           compression type and defining a checksum for the overall data transfer.
- *         - Enhannced VBOXDNDGHSENDDATAMSG to support (rolling) checksums for the supplied data block.
- *         - VBOXDNDHGSENDFILEDATAMSG and VBOXDNDGHSENDFILEDATAMSG are now sharing the same HGCM mesasge.
- *         - VBOXDNDHGSENDDATAMSG and VBOXDNDGHSENDDATAMSG can now contain an optional checksum for the current data block.
+ *         + Enhannced VBOXDNDGHSENDDATAMSG to support (rolling) checksums for the supplied data block.
+ *         + VBOXDNDHGSENDDATAMSG and VBOXDNDGHSENDDATAMSG can now contain an optional checksum for the current data block.
+ *         | VBOXDNDHGSENDFILEDATAMSG and VBOXDNDGHSENDFILEDATAMSG are now sharing the same HGCM mesasge.
  *         - Removed unused HOST_DND_GH_RECV_DIR, HOST_DND_GH_RECV_FILE_DATA and HOST_DND_GH_RECV_FILE_HDR commands.
  */
@@ -184,7 +185,12 @@
 enum eGuestFn
 {
-    /* Guest sends a connection request to the HGCM service.
+    /* Guest sends a connection request to the HGCM service,
+     * along with some additional information like supported
+     * protocol version and flags.
      * Note: New since protocol version 2. */
     GUEST_DND_CONNECT                  = 10,
+
+    /* Sent when a guest client disconnected from the HGCM service. */
+    GUEST_DND_DISCONNECT               = 11,
 
     /**
@@ -287,5 +293,5 @@
             HGCMFunctionParameter uAllActions;  /* OUT uint32_t */
             HGCMFunctionParameter pvFormats;    /* OUT ptr */
-            HGCMFunctionParameter cFormats;     /* OUT uint32_t */
+            HGCMFunctionParameter cbFormats;    /* OUT uint32_t */
         } v1;
         struct
@@ -299,5 +305,5 @@
             HGCMFunctionParameter uAllActions;  /* OUT uint32_t */
             HGCMFunctionParameter pvFormats;    /* OUT ptr */
-            HGCMFunctionParameter cFormats;     /* OUT uint32_t */
+            HGCMFunctionParameter cbFormats;    /* OUT uint32_t */
         } v3;
     } u;
@@ -680,5 +686,6 @@
 
 /**
- * HG Acknowledge Operation event.
+ * Acknowledges a host operation along with the allowed
+ * action(s) on the guest.
  *
  * Used by:
@@ -923,4 +930,10 @@
     uint32_t                    uFlags;
 } VBOXDNDCBCONNECTMSGDATA, *PVBOXDNDCBCONNECTMSGDATA;
+
+typedef struct VBOXDNDCBDISCONNECTMSGDATA
+{
+    /** Callback data header. */
+    VBOXDNDCBHEADERDATA         hdr;
+} VBOXDNDCBDISCONNECTMSGDATA, *PVBOXDNDCBDISCONNECTMSGDATA;
 
 typedef struct VBOXDNDCBHGGETNEXTHOSTMSG
Index: /trunk/include/VBox/HostServices/Service.h
===================================================================
--- /trunk/include/VBox/HostServices/Service.h	(revision 58328)
+++ /trunk/include/VBox/HostServices/Service.h	(revision 58329)
@@ -300,5 +300,5 @@
     }
 
-private:
+protected:
 
     uint32_t m_uClientId;
@@ -310,4 +310,20 @@
     PVBOXHGCMSVCPARM m_paParms;
 };
+
+/**
+ * Structure for keeping a HGCM service context.
+ */
+typedef struct VBOXHGCMSVCTX
+{
+    /** HGCM helper functions. */
+    PVBOXHGCMSVCHELPERS pHelpers;
+    /*
+     * Callback function supplied by the host for notification of updates
+     * to properties.
+     */
+    PFNHGCMSVCEXT       pfnHostCallback;
+    /** User data pointer to be supplied to the host callback function. */
+    void               *pvHostData;
+} VBOXHGCMSVCTX, *PVBOXHGCMSVCTX;
 
 template <class T>
@@ -383,8 +399,8 @@
 protected:
     explicit AbstractService(PVBOXHGCMSVCHELPERS pHelpers)
-        : m_pHelpers(pHelpers)
-        , m_pfnHostCallback(NULL)
-        , m_pvHostData(NULL)
-    {}
+    {
+        RT_ZERO(m_SvcCtx);
+        m_SvcCtx.pHelpers = pHelpers;
+    }
     virtual int  init(VBOXHGCMSVCFNTABLE *ptable) { return VINF_SUCCESS; }
     virtual int  uninit() { return VINF_SUCCESS; }
@@ -396,13 +412,6 @@
     /** Type definition for use in callback functions. */
     typedef AbstractService SELF;
-    /** HGCM helper functions. */
-    PVBOXHGCMSVCHELPERS m_pHelpers;
-    /*
-     * Callback function supplied by the host for notification of updates
-     * to properties.
-     */
-    PFNHGCMSVCEXT m_pfnHostCallback;
-    /** User data pointer to be supplied to the host callback function. */
-    void *m_pvHostData;
+    /** The HGCM service context this service is bound to. */
+    VBOXHGCMSVCTX m_SvcCtx;
 
     /**
@@ -500,6 +509,6 @@
         LogFlowFunc(("pvService=%p, pfnExtension=%p, pvExtention=%p\n", pvService, pfnExtension, pvExtension));
         SELF *pSelf = reinterpret_cast<SELF *>(pvService);
-        pSelf->m_pfnHostCallback = pfnExtension;
-        pSelf->m_pvHostData = pvExtension;
+        pSelf->m_SvcCtx.pfnHostCallback = pfnExtension;
+        pSelf->m_SvcCtx.pvHostData      = pvExtension;
         return VINF_SUCCESS;
     }
Index: /trunk/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibDragAndDrop.cpp
===================================================================
--- /trunk/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibDragAndDrop.cpp	(revision 58328)
+++ /trunk/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibDragAndDrop.cpp	(revision 58329)
@@ -131,5 +131,5 @@
         Msg.u.v1.uAllActions.SetUInt32(0);
         Msg.u.v1.pvFormats.SetPtr(pszFormats, cbFormats);
-        Msg.u.v1.cFormats.SetUInt32(0);
+        Msg.u.v1.cbFormats.SetUInt32(0);
     }
     else
@@ -144,5 +144,5 @@
         Msg.u.v3.uAllActions.SetUInt32(0);
         Msg.u.v3.pvFormats.SetPtr(pszFormats, cbFormats);
-        Msg.u.v3.cFormats.SetUInt32(0);
+        Msg.u.v3.cbFormats.SetUInt32(0);
     }
 
@@ -160,5 +160,5 @@
                 rc = Msg.u.v1.uDefAction.GetUInt32(puDefAction);   AssertRC(rc);
                 rc = Msg.u.v1.uAllActions.GetUInt32(puAllActions); AssertRC(rc);
-                rc = Msg.u.v1.cFormats.GetUInt32(pcbFormatsRecv);  AssertRC(rc);
+                rc = Msg.u.v1.cbFormats.GetUInt32(pcbFormatsRecv); AssertRC(rc);
             }
             else
@@ -170,5 +170,5 @@
                 rc = Msg.u.v3.uDefAction.GetUInt32(puDefAction);   AssertRC(rc);
                 rc = Msg.u.v3.uAllActions.GetUInt32(puAllActions); AssertRC(rc);
-                rc = Msg.u.v3.cFormats.GetUInt32(pcbFormatsRecv);  AssertRC(rc);
+                rc = Msg.u.v3.cbFormats.GetUInt32(pcbFormatsRecv); AssertRC(rc);
             }
 
@@ -447,8 +447,34 @@
 }
 
-static int vbglR3DnDHGRecvURIData(PVBGLR3GUESTDNDCMDCTX pCtx, DnDDroppedFiles *pDroppedFiles)
+static int vbglR3DnDHGRecvURIData(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr, DnDDroppedFiles *pDroppedFiles)
 {
     AssertPtrReturn(pCtx,          VERR_INVALID_POINTER);
+    AssertPtrReturn(pDataHdr,      VERR_INVALID_POINTER);
     AssertPtrReturn(pDroppedFiles, VERR_INVALID_POINTER);
+
+    /* Only count the raw data minus the already received meta data. */
+    Assert(pDataHdr->cbTotal >= pDataHdr->cbMeta);
+    uint64_t cbToRecvBytes = pDataHdr->cbTotal - pDataHdr->cbMeta;
+    uint64_t cToRecvObjs   = pDataHdr->cObjects;
+
+    LogFlowFunc(("cbToRecvBytes=%RU64, cToRecvObjs=%RU64, (cbTotal=%RU64, cbMeta=%RU32)\n",
+                 cbToRecvBytes, cToRecvObjs, pDataHdr->cbTotal, pDataHdr->cbMeta));
+
+    /*
+     * Only do accounting for protocol v3 and up.
+     * The older protocols did not have any data accounting available, so
+     * we simply tried to receive as much data as available and bail out.
+     */
+    const bool fDoAccounting = pCtx->uProtocol >= 3;
+
+    /* Anything to do at all? */
+    if (fDoAccounting)
+    {
+        if (   !cbToRecvBytes
+            && !cToRecvObjs)
+        {
+            return VINF_SUCCESS;
+        }
+    }
 
     /*
@@ -484,12 +510,21 @@
     char szPathName[RTPATH_MAX] = { 0 };
     uint32_t cbPathName = 0;
-    uint32_t fFlags = 0;
-    uint32_t fMode = 0;
-
-    while (RT_SUCCESS(rc))
-    {
+    uint32_t fFlags     = 0;
+    uint32_t fMode      = 0;
+
+    /*
+     * Only wait for new incoming commands for protocol v3 and up.
+     * The older protocols did not have any data accounting available, so
+     * we simply tried to receive as much data as available and bail out.
+     */
+    const bool fWait = pCtx->uProtocol >= 3;
+
+    do
+    {
+        LogFlowFunc(("Wating for new message ...\n"));
+
         uint32_t uNextMsg;
         uint32_t cNextParms;
-        rc = vbglR3DnDGetNextMsgType(pCtx, &uNextMsg, &cNextParms, false /* fWait */);
+        rc = vbglR3DnDGetNextMsgType(pCtx, &uNextMsg, &cNextParms, fWait);
         if (RT_SUCCESS(rc))
         {
@@ -519,4 +554,11 @@
                         if (RT_SUCCESS(rc))
                             rc = pDroppedFiles->AddDir(pszPathAbs);
+
+                        if (   RT_SUCCESS(rc)
+                            && fDoAccounting)
+                        {
+                            Assert(cToRecvObjs);
+                            cToRecvObjs--;
+                        }
 
                         RTStrFree(pszPathAbs);
@@ -623,4 +665,10 @@
                                 /* Data transfer complete? Close the file. */
                                 fClose = objFile.IsComplete();
+                                if (   fClose
+                                    && fDoAccounting)
+                                {
+                                    Assert(cToRecvObjs);
+                                    cToRecvObjs--;
+                                }
 
                                 /* Only since protocol v2 we know the file size upfront. */
@@ -631,4 +679,10 @@
 
                             cbFileWritten += cbChunkWritten;
+
+                            if (pCtx->uProtocol >= 3)
+                            {
+                                Assert(cbToRecvBytes >= cbChunkRead);
+                                cbToRecvBytes -= cbChunkRead;
+                            }
                         }
 
@@ -660,5 +714,15 @@
             break;
 
-    } /* while */
+        if (fDoAccounting)
+        {
+            LogFlowFunc(("cbToRecvBytes=%RU64, cToRecvObjs=%RU64\n", cbToRecvBytes, cToRecvObjs));
+            if (   !cbToRecvBytes
+                && !cToRecvObjs)
+            {
+                break;
+            }
+        }
+
+    } while (RT_SUCCESS(rc));
 
     LogFlowFunc(("Loop ended with %Rrc\n", rc));
@@ -723,5 +787,5 @@
         uint32_t cbDataRecv;
 
-        if (pCtx->uProtocol < 3) /* For VBox < 5.0.8. */
+        if (pCtx->uProtocol < 3)
         {
             Msg.hdr.cParms  = 5;
@@ -901,5 +965,7 @@
     uint32_t cbDataRecv;
 
-    if (pCtx->uProtocol < 3) /* For VBox < 5.0.8. */
+    LogFlowFuncEnter();
+
+    if (pCtx->uProtocol < 3)
     {
         uint64_t cbDataTmp = pCtx->cbMaxChunkSize;
@@ -979,10 +1045,10 @@
             RTMemFree(pvDataTmp);
     }
-    else /* Protocol v3 and up. Since VBox 5.0.8. */
+    else /* Protocol v3 and up. */
     {
         rc = vbglR3DnDHGRecvDataHdr(pCtx, pDataHdr);
         if (RT_SUCCESS(rc))
         {
-            LogFlowFunc(("cbMeta=%RU32\n", pDataHdr->cbMeta));
+            LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32\n", pDataHdr->cbTotal, pDataHdr->cbMeta));
             if (pDataHdr->cbMeta)
             {
@@ -1086,5 +1152,5 @@
             rc = lstURI.RootFromURIData(pvData, cbData, 0 /* fFlags */);
             if (RT_SUCCESS(rc))
-                rc = vbglR3DnDHGRecvURIData(pCtx, &droppedFiles);
+                rc = vbglR3DnDHGRecvURIData(pCtx, &dataHdr, &droppedFiles);
 
             if (RT_SUCCESS(rc)) /** @todo Remove this block as soon as we hand in DnDURIList. */
@@ -1506,4 +1572,6 @@
     Msg.hdr.u32Function = GUEST_DND_HG_ACK_OP;
 
+    LogFlowFunc(("uProto=%RU32\n", pCtx->uProtocol));
+
     if (pCtx->uProtocol < 3)
     {
@@ -1660,5 +1728,5 @@
 
     /* For protocol v3 and up we need to send the data header first. */
-    if (pCtx->uProtocol > 2)
+    if (pCtx->uProtocol >= 3)
     {
         AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
@@ -1675,5 +1743,5 @@
         Msg.uScreenId.SetUInt32(0);                          /** @todo Not used for guest->host (yet). */
         Msg.cbTotal.SetUInt64(pDataHdr->cbTotal);
-        Msg.cbMeta.SetUInt64(pDataHdr->cbMeta);
+        Msg.cbMeta.SetUInt32(pDataHdr->cbMeta);
         Msg.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
         Msg.cbMetaFmt.SetUInt32(pDataHdr->cbMetaFmt);
@@ -1700,5 +1768,5 @@
         Msg.hdr.u32Function = GUEST_DND_GH_SND_DATA;
 
-        if (pCtx->uProtocol > 2)
+        if (pCtx->uProtocol >= 3)
         {
             Msg.hdr.cParms = 5;
@@ -1721,5 +1789,5 @@
         uint32_t       cbSent     = 0;
 
-        HGCMFunctionParameter *pParm = (pCtx->uProtocol > 2)
+        HGCMFunctionParameter *pParm = (pCtx->uProtocol >= 3)
                                      ? &Msg.u.v3.pvData
                                      : &Msg.u.v1.pvData;
Index: /trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDHandler.cpp
===================================================================
--- /trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDHandler.cpp	(revision 58328)
+++ /trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDHandler.cpp	(revision 58329)
@@ -75,5 +75,5 @@
     : m_pSession(pSession)
     , m_pParent(pParent)
-    , m_enmMode(DNDMODE_UNKNOWN)
+    , m_enmOpMode(DNDMODE_UNKNOWN)
     , m_fIsPending(false)
     , m_fDataRetrieved(false)
@@ -99,9 +99,9 @@
                                        const QMimeData *pMimeData)
 {
-    LogFlowFunc(("enmMode=%RU32, screenID=%RU32, x=%d, y=%d, action=%ld\n",
-                 m_enmMode, screenID, x, y, toVBoxDnDAction(proposedAction)));
-
-    if (   m_enmMode != DNDMODE_UNKNOWN
-        && m_enmMode != DNDMODE_HOSTTOGUEST)
+    LogFlowFunc(("enmOpMode=%RU32, screenID=%RU32, x=%d, y=%d, action=%ld\n",
+                 m_enmOpMode, screenID, x, y, toVBoxDnDAction(proposedAction)));
+
+    if (   m_enmOpMode != DNDMODE_UNKNOWN
+        && m_enmOpMode != DNDMODE_HOSTTOGUEST)
         return Qt::IgnoreAction;
 
@@ -114,8 +114,11 @@
                                           pMimeData->formats().toVector());
     if (m_dndTarget.isOk())
-        setMode(DNDMODE_HOSTTOGUEST);
-
-    /* Set the DnD action returned by the guest. */
-    return toQtDnDAction(result);
+    {
+        setOpMode(DNDMODE_HOSTTOGUEST);
+        return toQtDnDAction(result);
+    }
+
+    msgCenter().cannotDropDataToGuest(m_dndTarget, m_pParent);
+    return Qt::IgnoreAction;
 }
 
@@ -124,8 +127,8 @@
                                       const QMimeData *pMimeData)
 {
-    LogFlowFunc(("enmMode=%RU32, screenID=%RU32, x=%d, y=%d, action=%ld\n",
-                 m_enmMode, screenID, x, y, toVBoxDnDAction(proposedAction)));
-
-    if (m_enmMode != DNDMODE_HOSTTOGUEST)
+    LogFlowFunc(("enmOpMode=%RU32, screenID=%RU32, x=%d, y=%d, action=%ld\n",
+                 m_enmOpMode, screenID, x, y, toVBoxDnDAction(proposedAction)));
+
+    if (m_enmOpMode != DNDMODE_HOSTTOGUEST)
         return Qt::IgnoreAction;
 
@@ -138,6 +141,9 @@
                                          toVBoxDnDActions(possibleActions),
                                          pMimeData->formats().toVector());
-    /* Set the DnD action returned by the guest. */
-    return toQtDnDAction(result);
+    if (m_dndTarget.isOk())
+        return toQtDnDAction(result);
+
+    msgCenter().cannotDropDataToGuest(m_dndTarget, m_pParent);
+    return Qt::IgnoreAction;
 }
 
@@ -146,8 +152,8 @@
                                       const QMimeData *pMimeData)
 {
-    LogFlowFunc(("enmMode=%RU32, screenID=%RU32, x=%d, y=%d, action=%ld\n",
-                 m_enmMode, screenID, x, y, toVBoxDnDAction(proposedAction)));
-
-    if (m_enmMode != DNDMODE_HOSTTOGUEST)
+    LogFlowFunc(("enmOpMode=%RU32, screenID=%RU32, x=%d, y=%d, action=%ld\n",
+                 m_enmOpMode, screenID, x, y, toVBoxDnDAction(proposedAction)));
+
+    if (m_enmOpMode != DNDMODE_HOSTTOGUEST)
         return Qt::IgnoreAction;
 
@@ -258,5 +264,5 @@
      * mode as well here.
      */
-    setMode(DNDMODE_UNKNOWN);
+    setOpMode(DNDMODE_UNKNOWN);
 
     return toQtDnDAction(enmResult);
@@ -265,10 +271,10 @@
 void UIDnDHandler::dragLeave(ulong screenID)
 {
-    LogFlowFunc(("enmMode=%RU32, screenID=%RU32\n", m_enmMode, screenID));
-
-    if (m_enmMode == DNDMODE_HOSTTOGUEST)
+    LogFlowFunc(("enmOpMode=%RU32, screenID=%RU32\n", m_enmOpMode, screenID));
+
+    if (m_enmOpMode == DNDMODE_HOSTTOGUEST)
     {
         m_dndTarget.Leave(screenID);
-        setMode(DNDMODE_UNKNOWN);
+        setOpMode(DNDMODE_UNKNOWN);
     }
 }
@@ -448,11 +454,11 @@
 #ifdef VBOX_WITH_DRAG_AND_DROP_GH
 
-    LogFlowFunc(("enmMode=%RU32, fIsPending=%RTbool, screenID=%RU32\n", m_enmMode, m_fIsPending, screenID));
+    LogFlowFunc(("enmOpMode=%RU32, fIsPending=%RTbool, screenID=%RU32\n", m_enmOpMode, m_fIsPending, screenID));
 
     {
         QMutexLocker AutoReadLock(&m_ReadLock);
 
-        if (   m_enmMode != DNDMODE_UNKNOWN
-            && m_enmMode != DNDMODE_GUESTTOHOST) /* Wrong mode set? */
+        if (   m_enmOpMode != DNDMODE_UNKNOWN
+            && m_enmOpMode != DNDMODE_GUESTTOHOST) /* Wrong mode set? */
             return VINF_SUCCESS;
 
@@ -548,5 +554,5 @@
     }
 
-    setMode(DNDMODE_GUESTTOHOST);
+    setOpMode(DNDMODE_GUESTTOHOST);
 
     rc = dragStartInternal(m_dataSource.lstFormats,
@@ -594,5 +600,5 @@
     m_fIsPending = false;
 
-    setMode(DNDMODE_UNKNOWN);
+    setOpMode(DNDMODE_UNKNOWN);
 }
 
@@ -684,6 +690,14 @@
                 /* After we successfully retrieved data from the source we query it from Main. */
                 vecData = m_dndSource.ReceiveData(); /** @todo QVector.size() is "int" only!? */
-                if (vecData.isEmpty())
-                    rc = VERR_NO_DATA;
+                if (m_dndSource.isOk())
+                {
+                    if (vecData.isEmpty())
+                        rc = VERR_NO_DATA;
+                }
+                else
+                {
+                    msgCenter().cannotDropDataToHost(m_dndSource, m_pParent);
+                    rc = VERR_GENERAL_FAILURE; /** @todo Fudge; do a GetResultCode() to rc translation. */
+                }
             }
             else
@@ -699,5 +713,5 @@
     }
 
-    setMode(DNDMODE_UNKNOWN);
+    setOpMode(DNDMODE_UNKNOWN);
 
     LogFlowFuncLeaveRC(rc);
@@ -705,9 +719,9 @@
 }
 
-void UIDnDHandler::setMode(DNDMODE enmMode)
+void UIDnDHandler::setOpMode(DNDOPMODE enmMode)
 {
     QMutexLocker AutoWriteLock(&m_WriteLock);
-    m_enmMode = enmMode;
-    LogFlowFunc(("Mode is now: %RU32\n", m_enmMode));
+    m_enmOpMode = enmMode;
+    LogFunc(("Operation mode is now: %RU32\n", m_enmOpMode));
 }
 
Index: /trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDHandler.h
===================================================================
--- /trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDHandler.h	(revision 58328)
+++ /trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDHandler.h	(revision 58329)
@@ -49,5 +49,5 @@
      *       drag and drop mode.
      */
-    typedef enum DNDMODE
+    typedef enum DNDOPMODE
     {
         /** Unknown mode. */
@@ -60,5 +60,5 @@
         /** The usual 32-bit type blow up. */
         DNDMODE_32BIT_HACK = 0x7fffffff
-    } DNDMODE;
+    } DNDOPMODE;
 
     /**
@@ -125,5 +125,5 @@
     int                        dragStartInternal(const QStringList &lstFormats, Qt::DropAction defAction, Qt::DropActions actions);
     int                        retrieveDataInternal(Qt::DropAction dropAction, const QString &strMIMEType, QVector<uint8_t> &vecData);
-    void                       setMode(DNDMODE enmMode);
+    void                       setOpMode(DNDOPMODE enmMode);
 
 protected:
@@ -133,11 +133,12 @@
     /** Pointer to parent widget. */
     QWidget          *m_pParent;
-
     /** Drag and drop source instance. */
     CDnDSource        m_dndSource;
     /** Drag and drop target instance. */
     CDnDTarget        m_dndTarget;
-    /** Current transfer direction. */
-    DNDMODE           m_enmMode;
+    /** Current operation mode, indicating the transfer direction.
+     *  Note: This is independent of the current drag and drop
+     *        mode being set for this VM! */
+    DNDOPMODE         m_enmOpMode;
     /** Current data from the source (if any).
      *  At the momenet we only support one source at a time. */
Index: /trunk/src/VBox/GuestHost/DragAndDrop/DnDURIList.cpp
===================================================================
--- /trunk/src/VBox/GuestHost/DragAndDrop/DnDURIList.cpp	(revision 58328)
+++ /trunk/src/VBox/GuestHost/DragAndDrop/DnDURIList.cpp	(revision 58329)
@@ -25,4 +25,5 @@
 #include <iprt/fs.h>
 #include <iprt/path.h>
+#include <iprt/string.h>
 #include <iprt/symlink.h>
 #include <iprt/uri.h>
@@ -448,6 +449,9 @@
     AssertReturn(cbData, VERR_INVALID_PARAMETER);
 
+    if (!RTStrIsValidEncoding(static_cast<const char *>(pvData)))
+        return VERR_INVALID_PARAMETER;
+
     RTCList<RTCString> lstURI =
-        RTCString(static_cast<const char*>(pvData), cbData - 1).split("\r\n");
+        RTCString(static_cast<const char *>(pvData), cbData - 1).split("\r\n");
     if (lstURI.isEmpty())
         return VINF_SUCCESS;
Index: /trunk/src/VBox/HostServices/DragAndDrop/service.cpp
===================================================================
--- /trunk/src/VBox/HostServices/DragAndDrop/service.cpp	(revision 58328)
+++ /trunk/src/VBox/HostServices/DragAndDrop/service.cpp	(revision 58329)
@@ -30,4 +30,6 @@
 #define LOG_GROUP LOG_GROUP_GUEST_DND
 
+#include <algorithm>
+#include <list>
 #include <map>
 
@@ -44,6 +46,44 @@
 *********************************************************************************************************************************/
 
-/** Map holding pointers to HGCM clients. Key is the (unique) HGCM client ID. */
-typedef std::map<uint32_t, HGCM::Client*> DnDClientMap;
+class DragAndDropClient : public HGCM::Client
+{
+public:
+
+    DragAndDropClient(uint32_t uClientId, VBOXHGCMCALLHANDLE hHandle = NULL,
+                      uint32_t uMsg = 0, uint32_t cParms = 0, VBOXHGCMSVCPARM aParms[] = NULL)
+        : HGCM::Client(uClientId, hHandle, uMsg, cParms, aParms)
+        , m_fDeferred(false)
+    {
+        RT_ZERO(m_SvcCtx);
+    }
+
+    virtual ~DragAndDropClient(void)
+    {
+        disconnect();
+    }
+
+public:
+
+    void complete(VBOXHGCMCALLHANDLE hHandle, int rcOp);
+    void completeDeferred(int rcOp);
+    void disconnect(void);
+    bool isDeferred(void) const { return m_fDeferred; }
+    void setDeferred(VBOXHGCMCALLHANDLE hHandle, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+    void setSvcContext(const HGCM::VBOXHGCMSVCTX &SvcCtx) { m_SvcCtx = SvcCtx; }
+
+protected:
+
+    /** The HGCM service context this client is bound to. */
+    HGCM::VBOXHGCMSVCTX m_SvcCtx;
+    /** Flag indicating whether this client currently is deferred mode,
+     *  meaning that it did not return to the caller yet. */
+    bool                m_fDeferred;
+};
+
+/** Map holding pointers to drag and drop clients. Key is the (unique) HGCM client ID. */
+typedef std::map<uint32_t, DragAndDropClient*> DnDClientMap;
+
+/** Simple queue (list) which holds deferred (waiting) clients. */
+typedef std::list<uint32_t> DnDClientQueue;
 
 /**
@@ -68,5 +108,5 @@
 
     int modeSet(uint32_t u32Mode);
-    inline uint32_t modeGet() { return m_u32Mode; };
+    inline uint32_t modeGet(void) const { return m_u32Mode; };
 
 protected:
@@ -76,13 +116,98 @@
 protected:
 
-    DnDManager             *m_pManager;
-    /** Map of all connected clients. */
-    DnDClientMap            m_clientMap;
+    /** Pointer to our DnD manager instance. */
+    DnDManager                        *m_pManager;
+    /** Map of all connected clients.
+     *  The primary key is the (unique) client ID, the secondary value
+     *  an allocated pointer to the DragAndDropClient class, managed
+     *  by this service class. */
+    DnDClientMap                       m_clientMap;
     /** List of all clients which are queued up (deferred return) and ready
-     *  to process new commands. */
-    RTCList<HGCM::Client*>  m_clientQueue;
-    uint32_t                m_u32Mode;
+     *  to process new commands. The key is the (unique) client ID. */
+    DnDClientQueue                     m_clientQueue;
+    /** Current drag and drop mode. */
+    uint32_t                           m_u32Mode;
 };
 
+
+/*********************************************************************************************************************************
+*   Client implementation                                                                                                        *
+*********************************************************************************************************************************/
+
+/**
+ * Completes the call by returning the control back to the guest
+ * side code.
+ */
+void DragAndDropClient::complete(VBOXHGCMCALLHANDLE hHandle, int rcOp)
+{
+    LogFlowThisFunc(("uClientID=%RU32\n", m_uClientId));
+
+    if (   m_SvcCtx.pHelpers
+        && m_SvcCtx.pHelpers->pfnCallComplete)
+    {
+        m_SvcCtx.pHelpers->pfnCallComplete(hHandle, rcOp);
+    }
+}
+
+/**
+ * Completes a deferred call by returning the control back to the guest
+ * side code.
+ */
+void DragAndDropClient::completeDeferred(int rcOp)
+{
+    AssertMsg(m_fDeferred, ("Client %RU32 is not in deferred mode\n", m_uClientId));
+    Assert(m_hHandle != NULL);
+
+    LogFlowThisFunc(("uClientID=%RU32\n", m_uClientId));
+
+    complete(m_hHandle, rcOp);
+    m_fDeferred = false;
+}
+
+/**
+ * Called when the HGCM client disconnected on the guest side.
+ * This function takes care of the client's data cleanup and also lets the host
+ * know that the client has been disconnected.
+ *
+ */
+void DragAndDropClient::disconnect(void)
+{
+    LogFlowThisFunc(("uClient=%RU32\n", m_uClientId));
+
+    if (isDeferred())
+        completeDeferred(VERR_INTERRUPTED);
+
+    /*
+     * Let the host know.
+     */
+    VBOXDNDCBDISCONNECTMSGDATA data;
+    RT_ZERO(data);
+    /** @todo Magic needed? */
+
+    if (m_SvcCtx.pfnHostCallback)
+    {
+        int rc2 = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, GUEST_DND_DISCONNECT, &data, sizeof(data));
+        if (RT_FAILURE(rc2))
+            LogFlowFunc(("Warning: Unable to notify host about client %RU32 disconnect, rc=%Rrc\n", m_uClientId, rc2));
+        /* Not fatal. */
+    }
+}
+
+/**
+ * Set the client's status to deferred, meaning that it does not return to the caller
+ * on the guest side yet.
+ */
+void DragAndDropClient::setDeferred(VBOXHGCMCALLHANDLE hHandle, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+    LogFlowThisFunc(("uClient=%RU32\n", m_uClientId));
+
+    AssertMsg(m_fDeferred == false, ("Client already in deferred mode\n"));
+    m_fDeferred = true;
+
+    m_hHandle = hHandle;
+    m_uMsg    = u32Function;
+    m_cParms  = cParms;
+    m_paParms = paParms;
+}
 
 /*********************************************************************************************************************************
@@ -118,4 +243,6 @@
 int DragAndDropService::uninit(void)
 {
+    LogFlowFuncEnter();
+
     if (m_pManager)
     {
@@ -124,4 +251,13 @@
     }
 
+    DnDClientMap::iterator itClient =  m_clientMap.begin();
+    while (itClient != m_clientMap.end())
+    {
+        delete itClient->second;
+        m_clientMap.erase(itClient);
+        itClient = m_clientMap.begin();
+    }
+
+    LogFlowFuncLeave();
     return VINF_SUCCESS;
 }
@@ -147,5 +283,7 @@
         try
         {
-            m_clientMap[u32ClientID] = new HGCM::Client(u32ClientID);
+            DragAndDropClient *pClient = new DragAndDropClient(u32ClientID);
+            pClient->setSvcContext(m_SvcCtx);
+            m_clientMap[u32ClientID] = pClient;
         }
         catch(std::bad_alloc &)
@@ -179,18 +317,5 @@
      * Remove from waiters queue.
      */
-    for (size_t i = 0; i < m_clientQueue.size(); i++)
-    {
-        HGCM::Client *pClient = m_clientQueue.at(i);
-        if (pClient->clientId() == u32ClientID)
-        {
-            if (m_pHelpers)
-                m_pHelpers->pfnCallComplete(pClient->handle(), VERR_INTERRUPTED);
-
-            m_clientQueue.removeAt(i);
-            delete pClient;
-
-            break;
-        }
-    }
+    m_clientQueue.remove(u32ClientID);
 
     /*
@@ -208,5 +333,13 @@
 int DragAndDropService::modeSet(uint32_t u32Mode)
 {
-    /** @todo Validate mode. */
+#ifndef VBOX_WITH_DRAG_AND_DROP_GH
+    if (   u32Mode == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST
+        || u32Mode == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL)
+    {
+        m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF;
+        return VERR_NOT_SUPPORTED;
+    }
+#endif
+
     switch (u32Mode)
     {
@@ -308,19 +441,30 @@
 #endif
 
-#define DO_HOST_CALLBACK();                                                     \
-    if (   RT_SUCCESS(rc)                                                       \
-        && m_pfnHostCallback)                                                   \
-    {                                                                           \
-        rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data)); \
+#define DO_HOST_CALLBACK();                                                                   \
+    if (   RT_SUCCESS(rc)                                                                     \
+        && m_SvcCtx.pfnHostCallback)                                                          \
+    {                                                                                         \
+        rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data)); \
+    }
+
+    /*
+     * Lookup client.
+     */
+    DragAndDropClient *pClient = NULL;
+
+    DnDClientMap::iterator itClient =  m_clientMap.find(u32ClientID);
+    if (itClient != m_clientMap.end())
+    {
+        pClient = itClient->second;
+        AssertPtr(pClient);
+    }
+    else
+    {
+        LogFunc(("Client %RU32 was not found\n", u32ClientID));
+        rc = VERR_NOT_FOUND;
     }
 
     if (rc == VINF_SUCCESS) /* Note: rc might be VINF_HGCM_ASYNC_EXECUTE! */
     {
-        DnDClientMap::iterator itClient =  m_clientMap.find(u32ClientID);
-        Assert(itClient != m_clientMap.end());
-
-        HGCM::Client *pClient = itClient->second;
-        AssertPtr(pClient);
-
         LogFlowFunc(("Client %RU32: Protocol v%RU32\n", pClient->clientId(), pClient->protocol()));
 
@@ -344,10 +488,10 @@
                     if (RT_FAILURE(rc)) /* No queued messages available? */
                     {
-                        if (m_pfnHostCallback) /* Try asking the host. */
+                        if (m_SvcCtx.pfnHostCallback) /* Try asking the host. */
                         {
                             VBOXDNDCBHGGETNEXTHOSTMSG data;
                             RT_ZERO(data);
                             data.hdr.uMagic = CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG;
-                            rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
+                            rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data));
                             if (RT_SUCCESS(rc))
                             {
@@ -366,9 +510,14 @@
                         if (RT_FAILURE(rc))
                         {
-                            if (paParms[2].u.uint32) /* Blocking flag set? */
+                            uint32_t fFlags = 0;
+                            int rc2 = paParms[2].getUInt32(&fFlags);
+                            if (   RT_SUCCESS(rc2)
+                                && fFlags) /* Blocking flag set? */
                             {
                                 /* Defer client returning. */
                                 rc = VINF_HGCM_ASYNC_EXECUTE;
                             }
+                            else
+                                rc = VERR_INVALID_PARAMETER;
 
                             LogFlowFunc(("Message queue is empty, returning %Rrc to guest\n", rc));
@@ -390,4 +539,6 @@
                     if (cParms >= 3)
                         rc = paParms[0].getUInt32(&data.hdr.uContextID);
+                    else /* Older protocols don't have a context ID. */
+                        rc = VINF_SUCCESS;
                     if (RT_SUCCESS(rc))
                         rc = paParms[idxProto].getUInt32(&data.uProtocol);
@@ -660,9 +811,9 @@
                             rc = paParms[0].getUInt32(&data.hdr.uContextID);
                             if (RT_SUCCESS(rc))
-                                rc = paParms[0].getPointer((void**)&data.pszPath, &data.cbPath);
-                            if (RT_SUCCESS(rc))
-                                rc = paParms[1].getUInt32(&data.cbPath);
-                            if (RT_SUCCESS(rc))
-                                rc = paParms[2].getUInt32(&data.fMode);
+                                rc = paParms[1].getPointer((void**)&data.pszPath, &data.cbPath);
+                            if (RT_SUCCESS(rc))
+                                rc = paParms[2].getUInt32(&data.cbPath);
+                            if (RT_SUCCESS(rc))
+                                rc = paParms[3].getUInt32(&data.fMode);
                         }
                         break;
@@ -842,5 +993,5 @@
                 if (rc == VERR_NO_DATA) /* Manager has no new messsages? Try asking the host. */
                 {
-                    if (m_pfnHostCallback)
+                    if (m_SvcCtx.pfnHostCallback)
                     {
                         VBOXDNDCBHGGETNEXTHOSTMSGDATA data;
@@ -851,5 +1002,6 @@
                         data.paParms = paParms;
 
-                        rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
+                        rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function,
+                                                      &data, sizeof(data));
                         if (RT_SUCCESS(rc))
                         {
@@ -875,7 +1027,7 @@
         try
         {
-            LogFlowFunc(("Deferring guest call completion of client ID=%RU32\n", u32ClientID));
-            m_clientQueue.append(new HGCM::Client(u32ClientID, callHandle,
-                                                  u32Function, cParms, paParms));
+            AssertPtr(pClient);
+            pClient->setDeferred(callHandle, u32Function, cParms, paParms);
+            m_clientQueue.push_back(u32ClientID);
         }
         catch (std::bad_alloc)
@@ -885,9 +1037,6 @@
         }
     }
-    else if (m_pHelpers)
-    {
-        /* Complete call on guest side. */
-        m_pHelpers->pfnCallComplete(callHandle, rc);
-    }
+    else if (pClient)
+        pClient->complete(callHandle, rc);
     else
         rc = VERR_NOT_IMPLEMENTED;
@@ -921,5 +1070,9 @@
                 if (m_clientQueue.size()) /* Any clients in our queue ready for processing the next command? */
                 {
-                    HGCM::Client *pClient = m_clientQueue.first();
+                    uint32_t uClientNext = m_clientQueue.front();
+                    DnDClientMap::iterator itClientNext = m_clientMap.find(uClientNext);
+                    Assert(itClientNext != m_clientMap.end());
+
+                    DragAndDropClient *pClient = itClientNext->second;
                     AssertPtr(pClient);
 
@@ -939,18 +1092,11 @@
                         if (RT_SUCCESS(rc))
                         {
-                            pClient->addMessageInfo(uMsg1, cParms1);
-                            if (   m_pHelpers
-                                && m_pHelpers->pfnCallComplete)
-                            {
-                                m_pHelpers->pfnCallComplete(pClient->handle(), rc);
-                            }
-
-                            m_clientQueue.removeFirst();
-
-                            delete pClient;
-                            pClient = NULL;
-                        }
-                        else
-                            AssertMsgFailed(("m_pManager::nextMessageInfo failed with rc=%Rrc\n", rc));
+                            rc = pClient->addMessageInfo(uMsg1, cParms1);
+
+                            /* Note: Report the current rc back to the guest. */
+                            pClient->completeDeferred(rc);
+
+                            m_clientQueue.pop_front();
+                        }
                     }
                     else
@@ -992,5 +1138,5 @@
     AssertPtr(pSelf);
 
-    if (pSelf->m_pfnHostCallback)
+    if (pSelf->m_SvcCtx.pfnHostCallback)
     {
         LogFlowFunc(("GUEST_DND_HG_EVT_PROGRESS: uStatus=%RU32, uPercentage=%RU32, rc=%Rrc\n",
@@ -1003,7 +1149,7 @@
         data.rc           = rc; /** @todo uin32_t vs. int. */
 
-        return pSelf->m_pfnHostCallback(pSelf->m_pvHostData,
-                                        GUEST_DND_HG_EVT_PROGRESS,
-                                        &data, sizeof(data));
+        return pSelf->m_SvcCtx.pfnHostCallback(pSelf->m_SvcCtx.pvHostData,
+                                               GUEST_DND_HG_EVT_PROGRESS,
+                                               &data, sizeof(data));
     }
 
Index: /trunk/src/VBox/Main/include/GuestDnDPrivate.h
===================================================================
--- /trunk/src/VBox/Main/include/GuestDnDPrivate.h	(revision 58328)
+++ /trunk/src/VBox/Main/include/GuestDnDPrivate.h	(revision 58329)
@@ -51,4 +51,8 @@
 typedef std::vector<com::Utf8Str> GuestDnDMIMEList;
 
+/*
+ ** @todo Put most of the implementations below in GuestDnDPrivate.cpp!
+ */
+
 class GuestDnDCallbackEvent
 {
@@ -100,4 +104,6 @@
     size_t add(const void *pvDataAdd, size_t cbDataAdd)
     {
+        LogFlowThisFunc(("pvDataAdd=%p, cbDataAdd=%zu\n", pvDataAdd, cbDataAdd));
+
         if (!cbDataAdd)
             return 0;
@@ -217,6 +223,7 @@
 
     GuestDnDData(void)
-        : cbProcessed(0)
-        , cbAddData(0)
+        : cbEstTotal(0)
+        , cbEstMeta(0)
+        , cbProcessed(0)
     {
         RT_ZERO(dataHdr);
@@ -241,4 +248,5 @@
     {
         const uint64_t cbTotal = getTotal();
+        LogFlowFunc(("cbProcessed=%RU64, cbTotal=%RU64\n", cbProcessed, cbTotal));
         Assert(cbProcessed <= cbTotal);
         return (cbProcessed == cbTotal);
@@ -257,5 +265,5 @@
     uint8_t getPercentComplete(void) const
     {
-        int64_t cbTotal  = RT_MAX(getTotal(), 1);
+        int64_t cbTotal = RT_MAX(getTotal(), 1);
         return (uint8_t)(cbProcessed * 100 / cbTotal);
     }
@@ -270,5 +278,5 @@
     }
 
-    uint64_t getTotal(void) const { return dataMeta.getSize() + cbAddData; }
+    uint64_t getTotal(void) const { return cbEstTotal; }
 
     void reset(void)
@@ -280,6 +288,8 @@
 
         dataMeta.reset();
+
+        cbEstTotal  = 0;
+        cbEstMeta   = 0;
         cbProcessed = 0;
-        cbAddData   = 0;
     }
 
@@ -306,23 +316,12 @@
     }
 
-    void setAdditionalSize(uint64_t cbAdd)
-    {
-        LogFlowFunc(("cbAdd=%RU64\n", cbAdd));
-
-        cbAddData = cbAdd;
-
-        invalidate();
-    }
-
     void setEstimatedSize(uint64_t cbTotal, uint32_t cbMeta)
     {
         Assert(cbMeta <= cbTotal);
 
-        LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU64\n", cbTotal, cbMeta));
-
-        dataMeta.reset();
-
-        dataHdr.cbMeta = cbMeta;
-        setAdditionalSize(cbTotal - cbMeta);
+        LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32\n", cbTotal, cbMeta));
+
+        cbEstTotal = cbTotal;
+        cbEstMeta  = cbMeta;
     }
 
@@ -351,13 +350,4 @@
 
         dataHdr.cbMetaFmt = 0;
-    }
-
-    void invalidate(void)
-    {
-        /* Update data header. */
-        dataHdr.cbMeta  = dataMeta.getSize();
-        dataHdr.cbTotal = dataHdr.cbMeta + cbAddData;
-
-        LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32\n", dataHdr.cbTotal, dataHdr.cbMeta));
     }
 
@@ -370,10 +360,16 @@
      *  according to the format being sent. */
     GuestDnDMetaData  dataMeta;
+    /** Estimated total data size when receiving data. */
+    uint64_t          cbEstTotal;
+    /** Estimated meta data size when receiving data. */
+    uint32_t          cbEstMeta;
     /** Overall size (in bytes) of processed data. */
     uint64_t          cbProcessed;
-    /** Additional data to process which does not count
-     *  as meta data. Just in here for accounting reasons. */
-    uint64_t          cbAddData;
 };
+
+/** Initial state. */
+#define DND_OBJCTX_STATE_NONE           0
+/** The header was received/sent. */
+#define DND_OBJCTX_STATE_HAS_HDR        RT_BIT(0)
 
 /**
@@ -387,5 +383,5 @@
         : pObjURI(NULL)
         , fIntermediate(false)
-        , fHeaderSent(false) { }
+        , fState(DND_OBJCTX_STATE_NONE) { }
 
     virtual ~GuestDnDURIObjCtx(void)
@@ -396,31 +392,42 @@
 public:
 
-    DnDURIObject *createIntermediate(void)
-    {
+    int createIntermediate(DnDURIObject::Type enmType = DnDURIObject::Unknown)
+    {
+        LogFlowThisFuncEnter();
+
         reset();
 
+        int rc;
+
         try
         {
-            pObjURI       = new DnDURIObject();
+            pObjURI       = new DnDURIObject(enmType);
             fIntermediate = true;
+
+            rc = VINF_SUCCESS;
         }
         catch (std::bad_alloc &)
         {
-        }
-
-        return pObjURI;
+            rc = VERR_NO_MEMORY;
+        }
+
+        return rc;
     }
 
     void destroy(void)
     {
+        LogFlowThisFuncEnter();
+
         if (   pObjURI
             && fIntermediate)
         {
             delete pObjURI;
-            fIntermediate = false;
-        }
-
-        pObjURI = NULL;
-    }
+        }
+
+        pObjURI       = NULL;
+        fIntermediate = false;
+    }
+
+    DnDURIObject *getObj(void) { return pObjURI; }
 
     bool isIntermediate(void) { return fIntermediate; }
@@ -428,11 +435,33 @@
     bool isValid(void) const { return (pObjURI != NULL); }
 
+    uint32_t getState(void) const { return fState; }
+
     void reset(void)
     {
+        LogFlowThisFuncEnter();
+
         destroy();
 
         fIntermediate = false;
-        fHeaderSent   = false;
-    }
+        fState        = 0;
+    }
+
+    void setObj(DnDURIObject *pObj)
+    {
+        LogFlowThisFunc(("%p\n", pObj));
+
+        destroy();
+
+        pObjURI = pObj;
+    }
+
+    uint32_t setState(uint32_t fStateNew)
+    {
+        /** @todo Add validation. */
+        fState = fStateNew;
+        return fState;
+    }
+
+protected:
 
     /** Pointer to current object being handled. */
@@ -440,6 +469,6 @@
     /** Flag whether pObjURI needs deletion after use. */
     bool                      fIntermediate;
-    /** Flag whether the object's file header has been sent already. */
-    bool                      fHeaderSent;
+    /** Internal context state, corresponding to DND_OBJCTX_STATE_XXX. */
+    uint32_t                  fState;
     /** @todo Add more statistics / information here. */
 };
@@ -490,5 +519,9 @@
     bool isComplete(void) const
     {
-        LogFlowFunc(("cObjToProcess=%RU64, cObjProcessed=%RU64\n", cObjToProcess, cObjProcessed));
+        LogFlowFunc(("cObjProcessed=%RU64, cObjToProcess=%RU64\n", cObjProcessed, cObjToProcess));
+
+        if (!cObjToProcess) /* Always return true if we don't have an object count. */
+            return true;
+
         Assert(cObjProcessed <= cObjToProcess);
         return (cObjProcessed == cObjToProcess);
@@ -514,5 +547,5 @@
         {
             /* Point the context object to the current DnDURIObject to process. */
-            objCtx.pObjURI = pCurObj;
+            objCtx.setObj(pCurObj);
         }
         else
@@ -534,7 +567,4 @@
         {
             case DnDURIObject::Directory:
-                rc = processDirectory(Obj.GetDestPath().c_str(), Obj.GetMode());
-                break;
-
             case DnDURIObject::File:
                 rc = VINF_SUCCESS;
@@ -570,10 +600,10 @@
     }
 
-    void reset(uint64_t cObjs = 0)
-    {
-        cObjToProcess = cObjs;
+    void reset(void)
+    {
+        LogFlowFuncEnter();
+
+        cObjToProcess = 0;
         cObjProcessed = 0;
-
-        LogFlowFunc(("cObjToProcess=%RU64\n", cObjToProcess));
 
         droppedFiles.Close();
@@ -583,28 +613,62 @@
     }
 
-public:
-
-    int fromMetaData(const GuestDnDMetaData &Data)
+    void setEstimatedObjects(uint64_t cObjs)
+    {
+        Assert(cObjToProcess == 0);
+        cObjToProcess = cObjs;
+        LogFlowFunc(("cObjToProcess=%RU64\n", cObjs));
+    }
+
+public:
+
+    int fromLocalMetaData(const GuestDnDMetaData &Data)
     {
         reset();
 
-        size_t      cbList  = Data.getSize();
-        const char *pszList = (const char *)Data.getData();
-        if (   !pszList
-            || !cbList)
-        {
+        if (!Data.getSize())
             return VINF_SUCCESS;
-        }
-
-        if (!RTStrIsValidEncoding(pszList))
-            return VERR_INVALID_PARAMETER;
-
-        RTCList<RTCString> lstURIOrg = RTCString(pszList, cbList).split("\r\n");
-        if (lstURIOrg.isEmpty())
-            return VINF_SUCCESS;
-
-        /* Note: All files to be transferred will be kept open during the entire DnD
-         *       operation, also to keep the accounting right. */
-        return lstURI.AppendURIPathsFromList(lstURIOrg, DNDURILIST_FLAGS_KEEP_OPEN);
+
+        char *pszList;
+        int rc = RTStrCurrentCPToUtf8(&pszList, (const char *)Data.getData());
+        if (RT_FAILURE(rc))
+        {
+            LogFlowThisFunc(("String conversion failed with rc=%Rrc\n", rc));
+            return rc;
+        }
+
+        const size_t cbList = Data.getSize();
+        LogFlowThisFunc(("metaData=%p, cbList=%zu\n", &Data, cbList));
+
+        if (cbList)
+        {
+            RTCList<RTCString> lstURIOrg = RTCString(pszList, cbList).split("\r\n");
+            if (!lstURIOrg.isEmpty())
+            {
+                /* Note: All files to be transferred will be kept open during the entire DnD
+                 *       operation, also to keep the accounting right. */
+                rc = lstURI.AppendURIPathsFromList(lstURIOrg, DNDURILIST_FLAGS_KEEP_OPEN);
+                if (RT_SUCCESS(rc))
+                    cObjToProcess = lstURI.TotalCount();
+            }
+        }
+
+        RTStrFree(pszList);
+        return rc;
+    }
+
+    int fromRemoteMetaData(const GuestDnDMetaData &Data)
+    {
+        LogFlowFuncEnter();
+
+        int rc = lstURI.RootFromURIData(Data.getData(), Data.getSize(), 0 /* uFlags */);
+        if (RT_SUCCESS(rc))
+        {
+            const size_t cRootCount = lstURI.RootCount();
+            LogFlowFunc(("cRootCount=%zu, cObjToProcess=%RU64\n", cRootCount, cObjToProcess));
+            if (cRootCount > cObjToProcess)
+                rc = VERR_INVALID_PARAMETER;
+        }
+
+        return rc;
     }
 
@@ -909,4 +973,7 @@
 typedef std::map<uint32_t, GuestDnDCallback> GuestDnDCallbackMap;
 
+/** @todo r=andy This class needs to go, as this now is too inflexible when it comes to all
+ *               the callback handling/dispatching. It's part of the initial code and only adds
+ *               unnecessary complexity. */
 class GuestDnDResponse
 {
Index: /trunk/src/VBox/Main/src-client/ConsoleImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/ConsoleImpl.cpp	(revision 58328)
+++ /trunk/src/VBox/Main/src-client/ConsoleImpl.cpp	(revision 58329)
@@ -7385,5 +7385,5 @@
 #endif // 0
 
-        
+
         /* setup task object and thread to carry out the operation
          * asynchronously */
@@ -8590,6 +8590,8 @@
 
     int rc = pVMMDev->hgcmHostCall("VBoxDragAndDropSvc",
-                                   DragAndDropSvc::HOST_DND_SET_MODE, 1, &parm);
-    LogFlowFunc(("rc=%Rrc\n", rc));
+                                   DragAndDropSvc::HOST_DND_SET_MODE, 1 /* cParms */, &parm);
+    if (RT_FAILURE(rc))
+        LogRel(("Error changing drag and drop mode: %Rrc\n", rc));
+
     return rc;
 }
Index: /trunk/src/VBox/Main/src-client/GuestDnDPrivate.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/GuestDnDPrivate.cpp	(revision 58328)
+++ /trunk/src/VBox/Main/src-client/GuestDnDPrivate.cpp	(revision 58329)
@@ -312,5 +312,7 @@
                 case DragAndDropSvc::DND_PROGRESS_CANCELLED:
                 {
-                    hr = m_pProgress->i_notifyComplete(S_OK);
+                    hr = m_pProgress->Cancel();
+                    AssertComRC(hr);
+                    hr = m_pProgress->i_notifyComplete(S_FALSE);
                     AssertComRC(hr);
 
@@ -364,6 +366,15 @@
         case DragAndDropSvc::GUEST_DND_CONNECT:
         {
-            /* Not used in here (yet). */
+            LogThisFunc(("Client connected\n"));
+
+            /* Nothing to do here (yet). */
             rc = VINF_SUCCESS;
+            break;
+        }
+
+        case DragAndDropSvc::GUEST_DND_DISCONNECT:
+        {
+            LogThisFunc(("Client disconnected\n"));
+            rc = setProgress(100, DND_PROGRESS_CANCELLED, VINF_SUCCESS);
             break;
         }
@@ -836,8 +847,9 @@
         && (uRevAdditions = m_pGuest->i_getAdditionsRevision()) > 0)
     {
-#ifdef _DEBUG
-# if 0
+#ifdef DEBUG
+# if 1
         /* Hardcode the to-used protocol version; nice for testing side effects. */
         uProto = 3;
+        rc = VINF_SUCCESS;
 # endif
 #endif
@@ -851,5 +863,5 @@
                 }
                 else
-                    uProto = 2; /* VBox 5.0.0 - 5.0.6: Protocol v2. */
+                    uProto = 2; /* VBox 5.0.0 - 5.0.8: Protocol v2. */
             }
 
@@ -940,5 +952,5 @@
 
     LogFlowFunc(("cbTotal=%RU64, cbProcessed=%RU64, cbRemaining=%RU64, cbDataAdd=%RU32\n",
-                 pData->getProcessed(), pData->getProcessed(), pData->getRemaining(), cbDataAdd));
+                 pData->getTotal(), pData->getProcessed(), pData->getRemaining(), cbDataAdd));
 
     if (!pResp)
Index: /trunk/src/VBox/Main/src-client/GuestDnDSourceImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/GuestDnDSourceImpl.cpp	(revision 58328)
+++ /trunk/src/VBox/Main/src-client/GuestDnDSourceImpl.cpp	(revision 58329)
@@ -407,4 +407,6 @@
     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
+    LogFlowThisFunc(("cTransfersPending=%RU32\n", mDataBase.m_cTransfersPending));
+
     /* Don't allow receiving the actual data until our transfer actually is complete. */
     if (mDataBase.m_cTransfersPending)
@@ -412,14 +414,5 @@
 
     PRECVDATACTX pCtx = &mData.mRecvCtx;
-
-    if (pCtx->mData.getMeta().getSize() == 0)
-    {
-        LogFlowFunc(("No data available, returning 0\n"));
-        aData.resize(0);
-        return S_OK;
-    }
-
     HRESULT hr = S_OK;
-    size_t cbData;
 
     try
@@ -435,9 +428,14 @@
         else
         {
-            cbData = pCtx->mData.getMeta().getSize();
-
-            /* Copy the data into a safe array of bytes. */
-            aData.resize(cbData);
-            memcpy(&aData.front(), pCtx->mData.getMeta().getData(), cbData);
+            const size_t cbData = pCtx->mData.getMeta().getSize();
+            LogFlowFunc(("cbData=%zu\n", cbData));
+            if (cbData)
+            {
+                /* Copy the data into a safe array of bytes. */
+                aData.resize(cbData);
+                memcpy(&aData.front(), pCtx->mData.getMeta().getData(), cbData);
+            }
+            else
+                aData.resize(0);
         }
     }
@@ -447,5 +445,5 @@
     }
 
-    LogFlowFunc(("Returning cbData=%zu, hr=%Rhrc\n", cbData, hr));
+    LogFlowFunc(("Returning hr=%Rhrc\n", hr));
     return hr;
 #endif /* VBOX_WITH_DRAG_AND_DROP */
@@ -536,5 +534,6 @@
 
     Assert(pCtx->mURI.getObjToProcess() == 0);
-    pCtx->mURI.reset(pDataHdr->cObjects);
+    pCtx->mURI.reset();
+    pCtx->mURI.setEstimatedObjects(pDataHdr->cObjects);
 
     /** @todo Handle compression type. */
@@ -554,44 +553,44 @@
     try
     {
-        GuestDnDData     *pData = &pCtx->mData;
-        GuestDnDURIData  *pURI  = &pCtx->mURI;
+        GuestDnDData    *pData = &pCtx->mData;
+        GuestDnDURIData *pURI  = &pCtx->mURI;
 
         uint32_t cbData;
         void    *pvData;
-        uint64_t cbToProcess;
+        uint64_t cbTotal;
         uint32_t cbMeta;
 
         if (mDataBase.m_uProtocolVersion < 3)
         {
-            cbData = pSndData->u.v1.cbData;
-            pvData = pSndData->u.v1.pvData;
+            cbData  = pSndData->u.v1.cbData;
+            pvData  = pSndData->u.v1.pvData;
 
             /* Sends the total data size to receive for every data chunk. */
-            cbToProcess = pSndData->u.v1.cbTotalSize;
+            cbTotal = pSndData->u.v1.cbTotalSize;
 
             /* Meta data size always is cbData, meaning there cannot be an
              * extended data chunk transfer by sending further data. */
-            cbMeta      = cbData;
+            cbMeta  = cbData;
         }
         else
         {
-            cbData = pSndData->u.v3.cbData;
-            pvData = pSndData->u.v3.pvData;
+            cbData  = pSndData->u.v3.cbData;
+            pvData  = pSndData->u.v3.pvData;
 
             /* Note: Data sizes get updated in i_onReceiveDataHdr(). */
-            cbToProcess = pData->getTotal();
-            cbMeta      = pData->getMeta().getSize();
-        }
-        Assert(cbToProcess);
+            cbTotal = pData->getTotal();
+            cbMeta  = pData->getMeta().getSize();
+        }
+        Assert(cbTotal);
 
         if (   cbData == 0
-            || cbData >  cbToProcess /* Paranoia */)
+            || cbData >  cbTotal /* Paranoia */)
         {
             LogFlowFunc(("Incoming data size invalid: cbData=%RU32, cbToProcess=%RU64\n", cbData, pData->getTotal()));
             rc = VERR_INVALID_PARAMETER;
         }
-        else if (cbToProcess < cbMeta)
-        {
-            AssertMsgFailed(("cbToProcess (%RU64) is smaller than meta size (%zu)\n", cbToProcess, cbMeta));
+        else if (cbTotal < cbMeta)
+        {
+            AssertMsgFailed(("cbTotal (%RU64) is smaller than cbMeta (%RU32)\n", cbTotal, cbMeta));
             rc = VERR_INVALID_PARAMETER;
         }
@@ -599,12 +598,14 @@
         if (RT_SUCCESS(rc))
         {
-            pData->getMeta().add(pvData, cbData);
-            LogFlowThisFunc(("cbMetaSize=%zu, cbData=%RU32, cbMeta=%RU32, cbToProcess=%RU64\n",
-                             pData->getMeta().getSize(), cbData, cbMeta, cbToProcess));
+            cbMeta = pData->getMeta().add(pvData, cbData);
+            LogFlowThisFunc(("cbMetaSize=%zu, cbData=%RU32, cbMeta=%RU32, cbTotal=%RU64\n",
+                             pData->getMeta().getSize(), cbData, cbMeta, cbTotal));
         }
 
         if (RT_SUCCESS(rc))
         {
-            /* (Meta) Data transfer complete? */
+            /*
+             * (Meta) Data transfer complete?
+             */
             Assert(cbMeta <= pData->getMeta().getSize());
             if (cbMeta == pData->getMeta().getSize())
@@ -615,9 +616,9 @@
                 {
                     /* Try parsing the data as URI list. */
-                    rc = pURI->fromMetaData(pData->getMeta());
+                    rc = pURI->fromRemoteMetaData(pData->getMeta());
                     if (RT_SUCCESS(rc))
                     {
                         if (mDataBase.m_uProtocolVersion < 3)
-                            pData->setEstimatedSize(cbToProcess, cbMeta);
+                            pData->setEstimatedSize(cbTotal, cbMeta);
 
                         /*
@@ -655,19 +656,29 @@
         || cbPath > RTPATH_MAX)
     {
+        LogFlowFunc(("Path length invalid, bailing out\n"));
         return VERR_INVALID_PARAMETER;
     }
 
-    if (!RTStrIsValidEncoding(pszPath))
+    int rc = RTStrValidateEncodingEx(pszPath, RTSTR_MAX, 0);
+    if (RT_FAILURE(rc))
+    {
+        LogFlowFunc(("Path validation failed with %Rrc, bailing out\n", rc));
         return VERR_INVALID_PARAMETER;
+    }
 
     if (pCtx->mURI.isComplete())
+    {
+        LogFlowFunc(("Data transfer already complete, bailing out\n"));
         return VERR_INVALID_PARAMETER;
-
-    GuestDnDURIObjCtx objCtx = pCtx->mURI.getObj(0); /** @todo Fill in context ID. */
-    DnDURIObject *pObj       = objCtx.createIntermediate();
-    if (!pObj)
-        return VERR_NO_MEMORY;
-
-    int rc;
+    }
+
+    GuestDnDURIObjCtx &objCtx = pCtx->mURI.getObj(0); /** @todo Fill in context ID. */
+
+    rc = objCtx.createIntermediate(DnDURIObject::Directory);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    DnDURIObject *pObj = objCtx.getObj();
+    AssertPtr(pObj);
 
     const char *pszDroppedFilesDir = pCtx->mURI.getDroppedFiles().GetDirAbs();
@@ -682,9 +693,11 @@
         rc = RTDirCreateFullPath(pszDir, fMode);
         if (RT_SUCCESS(rc))
+        {
+            pCtx->mURI.processObject(*pObj);
+            objCtx.reset();
             LogRel2(("DnD: Created guest directory on host: %s\n", pszDir));
+        }
         else
             LogRel(("DnD: Error creating guest directory '%s' on host, rc=%Rrc\n", pszDir, rc));
-
-        pCtx->mURI.removeObjCurrent();
 
         RTStrFree(pszDir);
@@ -726,5 +739,5 @@
     }
 
-    if (pCtx->mURI.isComplete())
+    if (pCtx->mURI.getObjToProcess() && pCtx->mURI.isComplete())
         return VERR_INVALID_PARAMETER;
 
@@ -733,6 +746,6 @@
     do
     {
-        GuestDnDURIObjCtx objCtx = pCtx->mURI.getObj(0); /** @todo Fill in context ID. */
-        DnDURIObject *pObj       = objCtx.pObjURI;
+        GuestDnDURIObjCtx &objCtx = pCtx->mURI.getObj(0); /** @todo Fill in context ID. */
+        DnDURIObject *pObj        = objCtx.getObj();
 
         /*
@@ -760,10 +773,9 @@
          * Create new intermediate object to work with.
          */
-        pObj = objCtx.createIntermediate();
-        if (!pObj)
-            rc = VERR_NO_MEMORY;
-
+        rc = objCtx.createIntermediate();
         if (RT_SUCCESS(rc))
         {
+            pObj = objCtx.getObj();
+
             const char *pszDroppedFilesDir = pCtx->mURI.getDroppedFiles().GetDirAbs();
 
@@ -828,4 +840,6 @@
     int rc = VINF_SUCCESS;
 
+    LogFlowFunc(("pvData=%p, cbData=%RU32, cbBlockSize=%RU32\n", pvData, cbData, mData.mcbBlockSize));
+
     /*
      * Sanity checking.
@@ -836,10 +850,11 @@
     do
     {
-        GuestDnDURIObjCtx objCtx = pCtx->mURI.getObj(0); /** @todo Fill in context ID. */
-        DnDURIObject *pObj       = objCtx.pObjURI;
+        GuestDnDURIObjCtx &objCtx = pCtx->mURI.getObj(0); /** @todo Fill in context ID. */
+        DnDURIObject *pObj        = objCtx.getObj();
 
         if (!pObj)
         {
-            rc = VERR_INVALID_PARAMETER;
+            LogFlowFunc(("Warning: No current object set\n"));
+            rc = VERR_WRONG_ORDER;
             break;
         }
@@ -880,7 +895,6 @@
                 /** @todo Sanitize path. */
                 LogRel2(("DnD: File transfer to host complete: %s\n", pObj->GetDestPath().c_str()));
+                pCtx->mURI.processObject(*pObj);
                 objCtx.reset();
-
-                rc = VINF_EOF;
             }
         }
@@ -1028,4 +1042,6 @@
      * Register callbacks.
      */
+    REGISTER_CALLBACK(GUEST_DND_CONNECT);
+    REGISTER_CALLBACK(GUEST_DND_DISCONNECT);
     REGISTER_CALLBACK(GUEST_DND_GH_EVT_ERROR);
     REGISTER_CALLBACK(GUEST_DND_GH_SND_DATA);
@@ -1059,4 +1075,6 @@
      * Unregister callbacks.
      */
+    UNREGISTER_CALLBACK(GUEST_DND_CONNECT);
+    UNREGISTER_CALLBACK(GUEST_DND_DISCONNECT);
     UNREGISTER_CALLBACK(GUEST_DND_GH_EVT_ERROR);
     UNREGISTER_CALLBACK(GUEST_DND_GH_SND_DATA);
@@ -1114,4 +1132,6 @@
      */
     /* Guest callbacks. */
+    REGISTER_CALLBACK(GUEST_DND_CONNECT);
+    REGISTER_CALLBACK(GUEST_DND_DISCONNECT);
     REGISTER_CALLBACK(GUEST_DND_GH_EVT_ERROR);
     if (mDataBase.m_uProtocolVersion >= 3)
@@ -1164,4 +1184,6 @@
      * Unregister callbacks.
      */
+    UNREGISTER_CALLBACK(GUEST_DND_CONNECT);
+    UNREGISTER_CALLBACK(GUEST_DND_DISCONNECT);
     UNREGISTER_CALLBACK(GUEST_DND_GH_EVT_ERROR);
     UNREGISTER_CALLBACK(GUEST_DND_GH_SND_DATA_HDR);
@@ -1224,4 +1246,12 @@
     switch (uMsg)
     {
+        case GUEST_DND_CONNECT:
+            /* Nothing to do here (yet). */
+            break;
+
+        case GUEST_DND_DISCONNECT:
+            rc = VERR_CANCELLED;
+            break;
+
 #ifdef VBOX_WITH_DRAG_AND_DROP_GH
         case GUEST_DND_GH_SND_DATA_HDR:
@@ -1297,4 +1327,12 @@
     switch (uMsg)
     {
+        case GUEST_DND_CONNECT:
+            /* Nothing to do here (yet). */
+            break;
+
+        case GUEST_DND_DISCONNECT:
+            rc = VERR_CANCELLED;
+            break;
+
 #ifdef VBOX_WITH_DRAG_AND_DROP_GH
         case GUEST_DND_GH_SND_DATA_HDR:
Index: /trunk/src/VBox/Main/src-client/GuestDnDTargetImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/GuestDnDTargetImpl.cpp	(revision 58328)
+++ /trunk/src/VBox/Main/src-client/GuestDnDTargetImpl.cpp	(revision 58329)
@@ -866,5 +866,5 @@
     AssertPtrReturn(pMsg,    VERR_INVALID_POINTER);
 
-    DnDURIObject *pObj = pObjCtx->pObjURI;
+    DnDURIObject *pObj = pObjCtx->getObj();
     AssertPtr(pObj);
 
@@ -893,5 +893,5 @@
     AssertPtrReturn(pMsg,    VERR_INVALID_POINTER);
 
-    DnDURIObject *pObj = pObjCtx->pObjURI;
+    DnDURIObject *pObj = pObjCtx->getObj();
     AssertPtr(pObj);
 
@@ -920,5 +920,6 @@
         if (mDataBase.m_uProtocolVersion >= 2)
         {
-            if (!pObjCtx->fHeaderSent)
+            uint32_t fState = pObjCtx->getState();
+            if (!(fState & DND_OBJCTX_STATE_HAS_HDR))
             {
                 /*
@@ -941,5 +942,5 @@
                 /** @todo Set progress object title to current file being transferred? */
 
-                pObjCtx->fHeaderSent = true;
+                pObjCtx->setState(fState | DND_OBJCTX_STATE_HAS_HDR);
             }
             else
@@ -972,5 +973,5 @@
     AssertPtrReturn(pMsg,    VERR_INVALID_POINTER);
 
-    DnDURIObject *pObj = pObjCtx->pObjURI;
+    DnDURIObject *pObj = pObjCtx->getObj();
     AssertPtr(pObj);
 
@@ -1058,4 +1059,12 @@
     switch (uMsg)
     {
+        case GUEST_DND_CONNECT:
+            /* Nothing to do here (yet). */
+            break;
+
+        case GUEST_DND_DISCONNECT:
+            rc = VERR_CANCELLED;
+            break;
+
         case GUEST_DND_GET_NEXT_HOST_MSG:
         {
@@ -1252,5 +1261,5 @@
     rc = pCtx->mpResp->setCallback(x, i_sendURIDataCallback, pCtx); \
     if (RT_FAILURE(rc))                                             \
-        break;
+        return rc;
 
 #define UNREGISTER_CALLBACK(x)                        \
@@ -1267,4 +1276,18 @@
     if (RT_FAILURE(rc))
         return rc;
+
+    /*
+     * Register callbacks.
+     */
+    /* Guest callbacks. */
+    REGISTER_CALLBACK(GUEST_DND_CONNECT);
+    REGISTER_CALLBACK(GUEST_DND_DISCONNECT);
+    REGISTER_CALLBACK(GUEST_DND_GET_NEXT_HOST_MSG);
+    REGISTER_CALLBACK(GUEST_DND_GH_EVT_ERROR);
+    /* Host callbacks. */
+    REGISTER_CALLBACK(HOST_DND_HG_SND_DIR);
+    if (mDataBase.m_uProtocolVersion >= 2)
+        REGISTER_CALLBACK(HOST_DND_HG_SND_FILE_HDR);
+    REGISTER_CALLBACK(HOST_DND_HG_SND_FILE_DATA);
 
     do
@@ -1276,5 +1299,5 @@
         GuestDnDURIData *pURI  = &pCtx->mURI;
 
-        rc = pURI->fromMetaData(pData->getMeta());
+        rc = pURI->fromLocalMetaData(pData->getMeta());
         if (RT_FAILURE(rc))
             break;
@@ -1291,9 +1314,14 @@
 
         /*
-         * Set the additional size we are going to send after the meta data header + meta data.
-         * This additional data will contain the actual file data we want to transfer.
+         * Set the estimated data sizes we are going to send.
+         * The total size also contains the meta data size.
          */
-        pData->setAdditionalSize(pURI->getURIList().TotalBytes());
-
+        const uint32_t cbMeta = pData->getMeta().getSize();
+        pData->setEstimatedSize(pURI->getURIList().TotalBytes() + cbMeta /* cbTotal */,
+                                                                  cbMeta /* cbMeta  */);
+
+        /*
+         * Set the meta format.
+         */
         void    *pvFmt = (void *)pCtx->mFmtReq.c_str();
         uint32_t cbFmt = pCtx->mFmtReq.length() + 1;        /* Include terminating zero. */
@@ -1326,45 +1354,36 @@
         if (RT_SUCCESS(rc))
         {
-            /*
-             * Register callbacks.
-             */
-            /* Guest callbacks. */
-            REGISTER_CALLBACK(GUEST_DND_GET_NEXT_HOST_MSG);
-            REGISTER_CALLBACK(GUEST_DND_GH_EVT_ERROR);
-            /* Host callbacks. */
-            REGISTER_CALLBACK(HOST_DND_HG_SND_DIR);
-            if (mDataBase.m_uProtocolVersion >= 2)
-                REGISTER_CALLBACK(HOST_DND_HG_SND_FILE_HDR);
-            REGISTER_CALLBACK(HOST_DND_HG_SND_FILE_DATA);
-
             rc = waitForEvent(&pCtx->mCBEvent, pCtx->mpResp, msTimeout);
-            if (RT_FAILURE(rc))
-            {
-                if (rc == VERR_CANCELLED)
-                    rc = pCtx->mpResp->setProgress(100, DND_PROGRESS_CANCELLED, VINF_SUCCESS);
-                else if (rc != VERR_GSTDND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
-                    rc = pCtx->mpResp->setProgress(100, DND_PROGRESS_ERROR, rc,
-                                                   GuestDnDTarget::i_hostErrorToString(rc));
-            }
-            else
-                rc = pCtx->mpResp->setProgress(100, DND_PROGRESS_COMPLETE, VINF_SUCCESS);
-
-            /*
-             * Unregister callbacks.
-             */
-            /* Guest callbacks. */
-            UNREGISTER_CALLBACK(GUEST_DND_GET_NEXT_HOST_MSG);
-            UNREGISTER_CALLBACK(GUEST_DND_GH_EVT_ERROR);
-            /* Host callbacks. */
-            UNREGISTER_CALLBACK(HOST_DND_HG_SND_DIR);
-            if (mDataBase.m_uProtocolVersion >= 2)
-                UNREGISTER_CALLBACK(HOST_DND_HG_SND_FILE_HDR);
-            UNREGISTER_CALLBACK(HOST_DND_HG_SND_FILE_DATA);
+            if (RT_SUCCESS(rc))
+                pCtx->mpResp->setProgress(100, DND_PROGRESS_COMPLETE, VINF_SUCCESS);
         }
 
     } while (0);
+
+    /*
+     * Unregister callbacks.
+     */
+    /* Guest callbacks. */
+    UNREGISTER_CALLBACK(GUEST_DND_CONNECT);
+    UNREGISTER_CALLBACK(GUEST_DND_DISCONNECT);
+    UNREGISTER_CALLBACK(GUEST_DND_GET_NEXT_HOST_MSG);
+    UNREGISTER_CALLBACK(GUEST_DND_GH_EVT_ERROR);
+    /* Host callbacks. */
+    UNREGISTER_CALLBACK(HOST_DND_HG_SND_DIR);
+    if (mDataBase.m_uProtocolVersion >= 2)
+        UNREGISTER_CALLBACK(HOST_DND_HG_SND_FILE_HDR);
+    UNREGISTER_CALLBACK(HOST_DND_HG_SND_FILE_DATA);
 
 #undef REGISTER_CALLBACK
 #undef UNREGISTER_CALLBACK
+
+    if (RT_FAILURE(rc))
+    {
+        if (rc == VERR_CANCELLED)
+            pCtx->mpResp->setProgress(100, DND_PROGRESS_CANCELLED, VINF_SUCCESS);
+        else if (rc != VERR_GSTDND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
+            pCtx->mpResp->setProgress(100, DND_PROGRESS_ERROR, rc,
+                                      GuestDnDTarget::i_hostErrorToString(rc));
+    }
 
     /*
@@ -1401,5 +1420,5 @@
         return VERR_WRONG_ORDER;
 
-    DnDURIObject *pCurObj = objCtx.pObjURI;
+    DnDURIObject *pCurObj = objCtx.getObj();
     AssertPtr(pCurObj);
 
