Index: trunk/src/VBox/Main/include/GuestDnDPrivate.h
===================================================================
--- trunk/src/VBox/Main/include/GuestDnDPrivate.h	(revision 55548)
+++ trunk/src/VBox/Main/include/GuestDnDPrivate.h	(revision 55549)
@@ -480,5 +480,5 @@
     int addMsg(GuestDnDMsg *pMsg)
     {
-        mDataBase.m_lstOutgoing.push_back(pMsg);
+        mDataBase.mListOutgoing.push_back(pMsg);
         return VINF_SUCCESS;
     }
@@ -486,17 +486,17 @@
     GuestDnDMsg *nextMsg(void)
     {
-        if (mDataBase.m_lstOutgoing.empty())
+        if (mDataBase.mListOutgoing.empty())
             return NULL;
-        return mDataBase.m_lstOutgoing.front();
+        return mDataBase.mListOutgoing.front();
     }
 
     void removeNext(void)
     {
-        if (!mDataBase.m_lstOutgoing.empty())
+        if (!mDataBase.mListOutgoing.empty())
         {
-            GuestDnDMsg *pMsg = mDataBase.m_lstOutgoing.front();
+            GuestDnDMsg *pMsg = mDataBase.mListOutgoing.front();
             if (pMsg)
                 delete pMsg;
-            mDataBase.m_lstOutgoing.pop_front();
+            mDataBase.mListOutgoing.pop_front();
         }
     }
@@ -504,5 +504,5 @@
 protected:
 
-    /** @name Attributes.
+    /** @name Public attributes (through getters/setters).
      * @{ */
     /** Pointer to guest implementation. */
@@ -514,9 +514,12 @@
     struct
     {
+        /** Flag indicating whether a drop operation currently
+         *  is in progress or not. */
+        bool                        mfTransferIsPending;
         /** The DnD protocol version to use, depending on the
          *  installed Guest Additions. */
         uint32_t                    mProtocolVersion;
         /** Outgoing message queue. */
-        GuestDnDMsgList             m_lstOutgoing;
+        GuestDnDMsgList             mListOutgoing;
     } mDataBase;
 };
Index: trunk/src/VBox/Main/include/GuestDnDSourceImpl.h
===================================================================
--- trunk/src/VBox/Main/include/GuestDnDSourceImpl.h	(revision 55548)
+++ trunk/src/VBox/Main/include/GuestDnDSourceImpl.h	(revision 55549)
@@ -93,7 +93,4 @@
     struct
     {
-        /** Flag indicating whether a drop operation currently
-         *  is in progress or not. */
-        bool        mfDropIsPending;
         /** Maximum data block size (in bytes) the source can handle. */
         uint32_t    mcbBlockSize;
Index: trunk/src/VBox/Main/include/GuestDnDTargetImpl.h
===================================================================
--- trunk/src/VBox/Main/include/GuestDnDTargetImpl.h	(revision 55548)
+++ trunk/src/VBox/Main/include/GuestDnDTargetImpl.h	(revision 55549)
@@ -85,4 +85,5 @@
     struct
     {
+        bool     mfTransferIsPending;
         /** Maximum data block size (in bytes) the target can handle. */
         uint32_t mcbBlockSize;
Index: trunk/src/VBox/Main/src-client/GuestDnDPrivate.cpp
===================================================================
--- trunk/src/VBox/Main/src-client/GuestDnDPrivate.cpp	(revision 55548)
+++ trunk/src/VBox/Main/src-client/GuestDnDPrivate.cpp	(revision 55549)
@@ -261,12 +261,14 @@
 {
     m_progress.setNull();
-    HRESULT rc = m_progress.createObject();
-    if (SUCCEEDED(rc))
-    {
-        rc = m_progress->init(static_cast<IGuest *>(pParent),
+
+    HRESULT hr = m_progress.createObject();
+    if (SUCCEEDED(hr))
+    {
+        hr = m_progress->init(static_cast<IGuest *>(pParent),
                               Bstr(pParent->tr("Dropping data")).raw(),
                               TRUE /* aCancelable */);
     }
-    return rc;
+
+    return hr;
 }
 
@@ -734,4 +736,9 @@
 GuestDnDBase::GuestDnDBase(void)
 {
+    mDataBase.mfTransferIsPending = false;
+
+    /*
+     * Initialize public attributes.
+     */
     m_strFormats = GuestDnDInst()->defaultFormats();
 }
Index: trunk/src/VBox/Main/src-client/GuestDnDSourceImpl.cpp
===================================================================
--- trunk/src/VBox/Main/src-client/GuestDnDSourceImpl.cpp	(revision 55548)
+++ trunk/src/VBox/Main/src-client/GuestDnDSourceImpl.cpp	(revision 55549)
@@ -101,5 +101,4 @@
      *       how to do something, so try to negogiate a sensible value here later. */
     mData.mcbBlockSize    = _64K; /** @todo Make this configurable. */
-    mData.mfDropIsPending = false;
 
     LogFlowThisFunc(("\n"));
@@ -293,21 +292,20 @@
 #else /* VBOX_WITH_DRAG_AND_DROP */
 
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
     /* Input validation. */
     if (RT_UNLIKELY((aFormat.c_str()) == NULL || *(aFormat.c_str()) == '\0'))
         return setError(E_INVALIDARG, tr("No drop format specified"));
 
-    AutoCaller autoCaller(this);
-    if (FAILED(autoCaller.rc())) return autoCaller.rc();
-
     uint32_t uAction = GuestDnD::toHGCMAction(aAction);
-    /* If there is no usable action, ignore this request. */
-    if (isDnDIgnoreAction(uAction))
+    if (isDnDIgnoreAction(uAction)) /* If there is no usable action, ignore this request. */
         return S_OK;
 
     /* Note: At the moment we only support one transfer at a time. */
-    if (ASMAtomicReadBool(&mData.mfDropIsPending))
+    if (ASMAtomicReadBool(&mDataBase.mfTransferIsPending))
         return setError(E_INVALIDARG, tr("Another drop operation already is in progress"));
 
-    ASMAtomicWriteBool(&mData.mfDropIsPending, true);
+    ASMAtomicWriteBool(&mDataBase.mfTransferIsPending, true);
 
     /* Dito. */
@@ -329,6 +327,5 @@
         AssertReturn(pTask->isOk(), pTask->getRC());
 
-        RTTHREAD recvThread;
-        int rc = RTThreadCreate(&recvThread, GuestDnDSource::i_receiveDataThread,
+        int rc = RTThreadCreate(NULL, GuestDnDSource::i_receiveDataThread,
                                 (void *)pTask, 0, RTTHREADTYPE_MAIN_WORKER, 0, "dndSrcRcvData");
         if (RT_SUCCESS(rc))
@@ -347,5 +344,5 @@
     }
 
-    /* Note: mData.mfDropIsPending will be set to false again by i_receiveDataThread. */
+    /* Note: mDataBase.mfTransferIsPending will be set to false again by i_receiveDataThread. */
 
     LogFlowFunc(("Returning hr=%Rhrc\n", hr));
@@ -365,6 +362,8 @@
     if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
-    if (ASMAtomicReadBool(&mData.mfDropIsPending))
-        return setError(E_INVALIDARG, tr("Current drop operation still running"));
+    /* Don't allow receiving the actual data until our transfer
+     * actually is complete. */
+    if (ASMAtomicReadBool(&mDataBase.mfTransferIsPending))
+        return setError(E_INVALIDARG, tr("Current drop operation still in progress"));
 
     PRECVDATACTX pCtx = &mData.mRecvCtx;
@@ -507,10 +506,12 @@
         {
             /*
-             * BUG: Protocol v1 does *not* send root directory names in URI format,
-             *      however, if this is a root URI directory (which came with the initial
-             *      GUEST_DND_GH_SND_DATA message(s)) the total data announced was for
-             *      root directory names which came in URI format, as an URI list.
+             * Protocols v1/v2 do *not* send root element names (files/directories)
+             * in URI format. The initial GUEST_DND_GH_SND_DATA message(s) however
+             * did take those element names into account, but *with* URI decoration
+             * when it comes to communicating the total bytes being sent.
              *
-             *      So construct an URI path locally to keep the accounting right.
+             * So translate the path into a valid URI path and add the resulting
+             * length (+ "\r\n" and termination) to the total bytes received
+             * to keep the accounting right.
              */
             char *pszPathURI = RTUriCreate("file" /* pszScheme */, "/" /* pszAuthority */,
@@ -593,5 +594,5 @@
         {
             /** @todo Unescpae path before printing. */
-            LogRel2(("DnD: Transferring file to host: %s\n", pCtx->mURI.objURI.GetDestPath().c_str()));
+            LogRel2(("DnD: Transferring guest file to host: %s\n", pCtx->mURI.objURI.GetDestPath().c_str()));
 
             /* Note: Protocol v1 does not send any file sizes, so always 0. */
@@ -604,7 +605,43 @@
         else
         {
-            LogRel2(("DnD: Error opening/creating guest file \"%s\" on host, rc=%Rrc\n",
+            LogRel2(("DnD: Error opening/creating guest file '%s' on host, rc=%Rrc\n",
                      pCtx->mURI.objURI.GetDestPath().c_str(), rc));
             break;
+        }
+
+        if (mDataBase.mProtocolVersion <= 2)
+        {
+            /*
+             * Protocols v1/v2 do *not* send root element names (files/directories)
+             * in URI format. The initial GUEST_DND_GH_SND_DATA message(s) however
+             * did take those element names into account, but *with* URI decoration
+             * when it comes to communicating the total bytes being sent.
+             *
+             * So translate the path into a valid URI path and add the resulting
+             * length (+ "\r\n" and termination) to the total bytes received
+             * to keep the accounting right.
+             */
+            char *pszPathURI = RTUriCreate("file" /* pszScheme */, "/" /* pszAuthority */,
+                                           pszPath /* pszPath */,
+                                           NULL /* pszQuery */, NULL /* pszFragment */);
+            if (pszPathURI)
+            {
+                bool fHasPath = RTPathHasPath(pszPath); /* Use original data received. */
+                if (!fHasPath) /* Root path? */
+                {
+                    cbPath  = strlen(pszPathURI);
+                    cbPath += 3;                  /* Include "\r" + "\n" + termination -- see above. */
+
+                    rc = i_updateProcess(pCtx, cbPath);
+                }
+
+                LogFlowFunc(("URI pszPathURI=%s, fHasPath=%RTbool, cbPath=%RU32\n", pszPathURI, fHasPath, cbPath));
+                RTStrFree(pszPathURI);
+            }
+            else
+            {
+                rc = VERR_NO_MEMORY;
+                break;
+            }
         }
 
@@ -667,5 +704,5 @@
         }
         else
-            LogRel(("DnD: Error: Can't write guest file to host to \"%s\": %Rrc\n", pCtx->mURI.objURI.GetDestPath().c_str(), rc));
+            LogRel(("DnD: Error writing guest file to host to \"%s\": %Rrc\n", pCtx->mURI.objURI.GetDestPath().c_str(), rc));
 
     } while (0);
@@ -749,11 +786,10 @@
         rc = VERR_COM_INVALID_OBJECT_STATE;
 
+    ASMAtomicWriteBool(&pSource->mDataBase.mfTransferIsPending, false);
+
     LogFlowFunc(("pSource=%p returning rc=%Rrc\n", (GuestDnDSource *)pSource, rc));
 
     if (pTask)
         delete pTask;
-
-    ASMAtomicWriteBool(&pSource->mData.mfDropIsPending, false);
-
     return rc;
 }
@@ -939,5 +975,4 @@
 
     int rc = VINF_SUCCESS;
-    bool fNotify = false;
 
     switch (uMsg)
@@ -974,7 +1009,4 @@
 
     if (RT_FAILURE(rc))
-        fNotify = true;
-
-    if (fNotify)
     {
         int rc2 = pCtx->mCallback.Notify(rc);
Index: trunk/src/VBox/Main/src-client/GuestDnDTargetImpl.cpp
===================================================================
--- trunk/src/VBox/Main/src-client/GuestDnDTargetImpl.cpp	(revision 55548)
+++ trunk/src/VBox/Main/src-client/GuestDnDTargetImpl.cpp	(revision 55549)
@@ -496,4 +496,6 @@
     else
         rc = VERR_COM_INVALID_OBJECT_STATE;
+
+    ASMAtomicWriteBool(&pTarget->mDataBase.mfTransferIsPending, false);
 
     LogFlowFunc(("pTarget=%p returning rc=%Rrc\n", (GuestDnDTarget *)pTarget, rc));
@@ -521,11 +523,20 @@
 #else /* VBOX_WITH_DRAG_AND_DROP */
 
-    /** @todo Add input validation. */
-    /** @todo Check if another sendData() call currently is being processed. */
-
     AutoCaller autoCaller(this);
     if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
-    /* Note: At the moment we only support one response at a time. */
+    /* Input validation. */
+    if (RT_UNLIKELY((aFormat.c_str()) == NULL || *(aFormat.c_str()) == '\0'))
+        return setError(E_INVALIDARG, tr("No data format specified"));
+    if (RT_UNLIKELY(!aData.size()))
+        return setError(E_INVALIDARG, tr("No data to send specified"));
+
+    /* Note: At the moment we only support one transfer at a time. */
+    if (ASMAtomicReadBool(&mDataBase.mfTransferIsPending))
+        return setError(E_INVALIDARG, tr("Another send operation already is in progress"));
+
+    ASMAtomicWriteBool(&mDataBase.mfTransferIsPending, true);
+
+    /* Dito. */
     GuestDnDResponse *pResp = GuestDnDInst()->response();
     AssertPtr(pResp);
@@ -549,6 +560,5 @@
         AssertReturn(pTask->isOk(), pTask->getRC());
 
-        RTTHREAD sendThread;
-        int rc = RTThreadCreate(&sendThread, GuestDnDTarget::i_sendDataThread,
+        int rc = RTThreadCreate(NULL, GuestDnDTarget::i_sendDataThread,
                                 (void *)pTask, 0, RTTHREADTYPE_MAIN_WORKER, 0, "dndTgtSndData");
         if (RT_SUCCESS(rc))
@@ -569,4 +579,6 @@
         hr = setError(E_OUTOFMEMORY);
     }
+
+    /* Note: mDataBase.mfTransferIsPending will be set to false again by i_sendDataThread. */
 
     LogFlowFunc(("Returning hr=%Rhrc\n", hr));
@@ -608,4 +620,7 @@
 
     ASMAtomicWriteBool(&pCtx->mIsActive, true);
+
+    /* Clear all remaining outgoing messages. */
+    mDataBase.mListOutgoing.clear();
 
     do
@@ -694,4 +709,6 @@
         rc = aFile.OpenEx(strPathSrc, DnDURIObject::File, DnDURIObject::Source,
                           RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, 0 /* fFlags */);
+        if (RT_FAILURE(rc))
+            LogRel2(("DnD: Error opening host file \"%s\", rc=%Rrc\n", strPathSrc.c_str(), rc));
     }
 
@@ -718,4 +735,6 @@
 
                 LogFlowFunc(("Sending file header ...\n"));
+                LogRel2(("DnD: Transferring host file to guest: %s (%RU64 bytes, mode 0x%x)\n",
+                         strPathSrc.c_str(), aFile.GetSize(), aFile.GetMode()));
             }
             else
@@ -800,4 +819,5 @@
         if (aFile.IsComplete()) /* Done reading? */
         {
+            LogRel2(("DnD: File transfer to guest complete: %s\n", aFile.GetSourcePath().c_str()));
             LogFlowFunc(("File \"%s\" complete\n", aFile.GetSourcePath().c_str()));
             rc = VINF_EOF;
@@ -821,5 +841,4 @@
 
     int rc = VINF_SUCCESS;
-    bool fNotify = false;
 
     switch (uMsg)
@@ -849,16 +868,5 @@
 
                 if (RT_FAILURE(rc))
-                {
-                    if (rc == VERR_NO_DATA) /* All URI objects processed? */
-                    {
-                        /* Unregister this callback. */
-                        AssertPtr(pCtx->mpResp);
-                        int rc2 = pCtx->mpResp->setCallback(uMsg, NULL /* PFNGUESTDNDCALLBACK */);
-                        if (RT_FAILURE(rc2))
-                            LogFlowFunc(("Error: Unable to unregister callback for message %RU32, rc=%Rrc\n", uMsg, rc2));
-                    }
-
                     delete pMsg;
-                }
             }
             catch(std::bad_alloc & /*e*/)
@@ -866,4 +874,17 @@
                 rc = VERR_NO_MEMORY;
             }
+            break;
+        }
+        case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
+        {
+            DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
+            AssertPtr(pCBData);
+            AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
+            AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
+
+            pCtx->mpResp->reset();
+            rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc);
+            if (RT_SUCCESS(rc))
+                rc = pCBData->rc;
             break;
         }
@@ -917,7 +938,28 @@
     }
 
-    if (fNotify)
-    {
-        int rc2 = pCtx->mCallback.Notify(rc);
+    if (RT_FAILURE(rc))
+    {
+        switch (rc)
+        {
+            case VERR_NO_DATA:
+                LogRel2(("DnD: Transfer complete\n"));
+                break;
+
+            case VERR_CANCELLED:
+                LogRel2(("DnD: Transfer canceled\n"));
+                break;
+
+            default:
+                LogRel(("DnD: Error %Rrc occurred, aborting transfer\n", rc));
+                break;
+        }
+
+        /* Unregister this callback. */
+        AssertPtr(pCtx->mpResp);
+        int rc2 = pCtx->mpResp->setCallback(uMsg, NULL /* PFNGUESTDNDCALLBACK */);
+        AssertRC(rc2);
+
+        /* Notify waiters. */
+        rc2 = pCtx->mCallback.Notify(rc);
         AssertRC(rc2);
     }
