Index: /trunk/src/VBox/HostServices/DragAndDrop/dndmanager.cpp
===================================================================
--- /trunk/src/VBox/HostServices/DragAndDrop/dndmanager.cpp	(revision 59831)
+++ /trunk/src/VBox/HostServices/DragAndDrop/dndmanager.cpp	(revision 59832)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2011-2015 Oracle Corporation
+ * Copyright (C) 2011-2016 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -41,110 +41,17 @@
 int DnDManager::addMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool fAppend /* = true */)
 {
-    int rc = VINF_SUCCESS;
-
-    LogFlowFunc(("uMsg=%RU32, cParms=%RU32, fAppend=%RTbool\n", uMsg, cParms, fAppend));
+    int rc;
 
     try
     {
-        DnDMessage *pMessage = NULL;
-
-        switch (uMsg)
-        {
-            case DragAndDropSvc::HOST_DND_HG_EVT_ENTER:
-            {
-                clear();
-                LogFlowFunc(("HOST_DND_HG_EVT_ENTER\n"));
-                break;
-            }
-
-            case DragAndDropSvc::HOST_DND_HG_EVT_MOVE:
-            {
-                LogFlowFunc(("HOST_DND_HG_EVT_MOVE\n"));
-                break;
-            }
-
-            case DragAndDropSvc::HOST_DND_HG_EVT_LEAVE:
-            {
-                LogFlowFunc(("HOST_DND_HG_EVT_LEAVE\n"));
-                break;
-            }
-
-            case DragAndDropSvc::HOST_DND_HG_EVT_DROPPED:
-            {
-                LogFlowFunc(("HOST_DND_HG_EVT_DROPPED\n"));
-                break;
-            }
-
-            case DragAndDropSvc::HOST_DND_HG_EVT_CANCEL:
-            {
-                LogFlowFunc(("HOST_DND_HG_EVT_CANCEL\n"));
-
-                pMessage = new DnDHGCancelMessage();
-                break;
-            }
-
-            case DragAndDropSvc::HOST_DND_HG_SND_DATA_HDR:
-            {
-                LogFlowFunc(("HOST_DND_HG_SND_DATA_HDR\n"));
-                break;
-            }
-
-            case DragAndDropSvc::HOST_DND_HG_SND_DATA:
-            {
-                LogFlowFunc(("HOST_DND_HG_SND_DATA\n"));
-                break;
-            }
-
-            case DragAndDropSvc::HOST_DND_HG_SND_DIR:
-            {
-                LogFlowFunc(("HOST_DND_HG_SND_DIR\n"));
-                break;
-            }
-
-            /* New since protocol version 2 (VBox 5.0). */
-            case DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR:
-            {
-                LogFlowFunc(("HOST_DND_HG_SND_FILE_HDR\n"));
-                break;
-            }
-
-            case DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA:
-            {
-                LogFlowFunc(("HOST_DND_HG_SND_FILE\n"));
-
-                /* No parameter verification here as, depending on the protocol version
-                 * being used, the parameter count + types might change. */
-                break;
-            }
-
-#ifdef VBOX_WITH_DRAG_AND_DROP_GH
-            case DragAndDropSvc::HOST_DND_GH_REQ_PENDING:
-            {
-                LogFlowFunc(("HOST_DND_GH_REQ_PENDING\n"));
-                break;
-            }
-
-            case DragAndDropSvc::HOST_DND_GH_EVT_DROPPED:
-            {
-                LogFlowFunc(("HOST_DND_GH_EVT_DROPPED\n"));
-                break;
-            }
-#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
-
-            default:
-                rc = VERR_NOT_IMPLEMENTED;
-                break;
-        }
-
-        if (RT_SUCCESS(rc))
-        {
-            if (!pMessage) /* Generic message needed? */
-                pMessage = new DnDGenericMessage(uMsg, cParms, paParms);
-
-            if (fAppend)
-                m_dndMessageQueue.append(pMessage);
-            else
-                m_dndMessageQueue.prepend(pMessage);
-        }
+        LogFlowFunc(("uMsg=%RU32, cParms=%RU32, fAppend=%RTbool\n", uMsg, cParms, fAppend));
+
+        DnDMessage *pMessage = new DnDGenericMessage(uMsg, cParms, paParms);
+        if (fAppend)
+            m_dndMessageQueue.append(pMessage);
+        else
+            m_dndMessageQueue.prepend(pMessage);
+
+        rc = VINF_SUCCESS;
     }
     catch(std::bad_alloc &)
@@ -153,4 +60,5 @@
     }
 
+    LogFlowFuncLeaveRC(rc);
     return rc;
 }
@@ -254,4 +162,6 @@
 void DnDManager::clear(void)
 {
+    LogFlowFuncEnter();
+
     if (m_pCurMsg)
     {
Index: /trunk/src/VBox/HostServices/DragAndDrop/service.cpp
===================================================================
--- /trunk/src/VBox/HostServices/DragAndDrop/service.cpp	(revision 59831)
+++ /trunk/src/VBox/HostServices/DragAndDrop/service.cpp	(revision 59832)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2011-2015 Oracle Corporation
+ * Copyright (C) 2011-2016 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -184,4 +184,5 @@
     RT_ZERO(data);
     /** @todo Magic needed? */
+    /** @todo Add context ID. */
 
     if (m_SvcCtx.pfnHostCallback)
@@ -987,4 +988,45 @@
             }
 #endif /* VBOX_WITH_DRAG_AND_DROP_GH */
+
+            /*
+             * Note: This is a fire-and-forget message, as the host should
+             *       not rely on an answer from the guest side in order to
+             *       properly cancel the operation.
+             */
+            case HOST_DND_HG_EVT_CANCEL:
+            {
+                LogFlowFunc(("HOST_DND_HG_EVT_CANCEL\n"));
+
+                VBOXDNDCBEVTERRORDATA data;
+                RT_ZERO(data);
+                data.hdr.uMagic = CB_MAGIC_DND_GH_EVT_ERROR;
+
+                switch (pClient->protocol())
+                {
+                    case 3:
+                    {
+                        /* Protocol v3+ at least requires the context ID. */
+                        if (cParms == 1)
+                            rc = paParms[0].getUInt32(&data.hdr.uContextID);
+
+                        break;
+                    }
+
+                    default:
+                        break;
+                }
+
+                /* Tell the host that the guest has cancelled the operation. */
+                data.rc = VERR_CANCELLED;
+
+                DO_HOST_CALLBACK();
+
+                /* Note: If the host is not prepared for handling the cancelling reply
+                 *       from the guest, don't report this back to the guest. */
+                if (RT_FAILURE(rc))
+                    rc = VINF_SUCCESS;
+                break;
+            }
+
             default:
             {
@@ -997,5 +1039,7 @@
                         VBOXDNDCBHGGETNEXTHOSTMSGDATA data;
                         RT_ZERO(data);
+
                         data.hdr.uMagic = VBOX_DND_CB_MAGIC_MAKE(0 /* uFn */, 0 /* uVer */);
+
                         data.uMsg    = u32Function;
                         data.cParms  = cParms;
@@ -1008,4 +1052,14 @@
                             cParms  = data.cParms;
                             paParms = data.paParms;
+                        }
+                        else
+                        {
+                            /*
+                             * In case the guest is too fast asking for the next message
+                             * and the host did not supply it yet, just defer the client's
+                             * return until a response from the host available.
+                             */
+                            LogFlowFunc(("No new messages from the host (yet), deferring request: %Rrc\n", rc));
+                            rc = VINF_HGCM_ASYNC_EXECUTE;
                         }
                     }
@@ -1040,5 +1094,8 @@
         pClient->complete(callHandle, rc);
     else
+    {
+        AssertMsgFailed(("Guest call failed with %Rrc\n", rc));
         rc = VERR_NOT_IMPLEMENTED;
+    }
 
     LogFlowFunc(("Returning rc=%Rrc\n", rc));
@@ -1052,78 +1109,159 @@
 
     int rc;
-    if (u32Function == HOST_DND_SET_MODE)
-    {
-        if (cParms != 1)
-            rc = VERR_INVALID_PARAMETER;
-        else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT)
-            rc = VERR_INVALID_PARAMETER;
-        else
-            rc = modeSet(paParms[0].u.uint32);
-    }
-    else if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
-    {
-        if (m_clientMap.size()) /* At least one client on the guest connected? */
+
+    do
+    {
+        bool fSendToGuest = false; /* Whether to send the message down to the guest side or not. */
+
+        switch (u32Function)
         {
+            case HOST_DND_SET_MODE:
+            {
+                if (cParms != 1)
+                    rc = VERR_INVALID_PARAMETER;
+                else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT)
+                    rc = VERR_INVALID_PARAMETER;
+                else
+                    rc = modeSet(paParms[0].u.uint32);
+                break;
+            }
+
+            case HOST_DND_HG_EVT_ENTER:
+            {
+                /* Clear the message queue as a new DnD operation just began. */
+                m_pManager->clear();
+
+                fSendToGuest = true;
+                break;
+            }
+
+            case HOST_DND_HG_EVT_CANCEL:
+            {
+                LogFlowFunc(("Cancelling all waiting clients ...\n"));
+
+                /* Clear the message queue as the host cancelled the whole operation. */
+                m_pManager->clear();
+
+                /*
+                 * Wake up all deferred clients and tell them to process
+                 * the cancelling message next.
+                 */
+                DnDClientQueue::iterator itQueue = m_clientQueue.begin();
+                while (itQueue != m_clientQueue.end())
+                {
+                    DnDClientMap::iterator itClient = m_clientMap.find(*itQueue);
+                    Assert(itClient != m_clientMap.end());
+
+                    DragAndDropClient *pClient = itClient->second;
+                    AssertPtr(pClient);
+
+                    int rc2 = pClient->addMessageInfo(HOST_DND_HG_EVT_CANCEL,
+                                                      /* Protocol v3+ also contains the context ID. */
+                                                      pClient->protocol() >= 3 ? 1 : 0);
+                    pClient->completeDeferred(rc2);
+
+                    m_clientQueue.erase(itQueue);
+                    itQueue = m_clientQueue.begin();
+                }
+
+                Assert(m_clientQueue.size() == 0);
+
+                /* Tell the host that everything went well. */
+                rc = VINF_SUCCESS;
+                break;
+            }
+
+            default:
+            {
+                fSendToGuest = true;
+                break;
+            }
+        }
+
+        if (fSendToGuest)
+        {
+            if (modeGet() == VBOX_DRAG_AND_DROP_MODE_OFF)
+            {
+                /* Tell the host that a wrong drag'n drop mode is set. */
+                rc = VERR_ACCESS_DENIED;
+                break;
+            }
+
+            if (m_clientMap.size() == 0) /* At least one client on the guest connected? */
+            {
+                /*
+                 * Tell the host that the guest does not support drag'n drop.
+                 * This might happen due to not installed Guest Additions or
+                 * not running VBoxTray/VBoxClient.
+                 */
+                rc = VERR_NOT_SUPPORTED;
+                break;
+            }
+
             rc = m_pManager->addMessage(u32Function, cParms, paParms, true /* fAppend */);
-            if (RT_SUCCESS(rc))
-            {
-                if (m_clientQueue.size()) /* Any clients in our queue ready for processing the next command? */
-                {
-                    uint32_t uClientNext = m_clientQueue.front();
-                    DnDClientMap::iterator itClientNext = m_clientMap.find(uClientNext);
-                    Assert(itClientNext != m_clientMap.end());
-
-                    DragAndDropClient *pClient = itClientNext->second;
-                    AssertPtr(pClient);
-
-                    /*
-                     * Check if this was a request for getting the next host
-                     * message. If so, return the message ID and the parameter
-                     * count. The message itself has to be queued.
-                     */
-                    uint32_t uMsg = pClient->message();
-                    if (uMsg == GUEST_DND_GET_NEXT_HOST_MSG)
-                    {
-                        LogFlowFunc(("Client %RU32 is waiting for next host msg\n", pClient->clientId()));
-
-                        uint32_t uMsg1;
-                        uint32_t cParms1;
-                        rc = m_pManager->nextMessageInfo(&uMsg1, &cParms1);
-                        if (RT_SUCCESS(rc))
-                        {
-                            rc = pClient->addMessageInfo(uMsg1, cParms1);
-
-                            /* Note: Report the current rc back to the guest. */
-                            pClient->completeDeferred(rc);
-
-                            m_clientQueue.pop_front();
-                        }
-                    }
-                    else
-                        AssertMsgFailed(("Client ID=%RU32 in wrong state with uMsg=%RU32\n",
-                                         pClient->clientId(), uMsg));
-                }
-                else
-                    LogFlowFunc(("All clients busy; delaying execution\n"));
-            }
-            else
-                AssertMsgFailed(("Adding new message of type=%RU32 failed with rc=%Rrc\n",
-                                 u32Function, rc));
-        }
-        else
-        {
+            if (RT_FAILURE(rc))
+            {
+                AssertMsgFailed(("Adding new message of type=%RU32 failed with rc=%Rrc\n", u32Function, rc));
+                break;
+            }
+
+            /* Any clients in our queue ready for processing the next command? */
+            if (m_clientQueue.size() == 0)
+            {
+                LogFlowFunc(("All clients (%zu) busy -- delaying execution\n", m_clientMap.size()));
+                break;
+            }
+
+            uint32_t uClientNext = m_clientQueue.front();
+            DnDClientMap::iterator itClientNext = m_clientMap.find(uClientNext);
+            Assert(itClientNext != m_clientMap.end());
+
+            DragAndDropClient *pClient = itClientNext->second;
+            AssertPtr(pClient);
+
             /*
-             * Tell the host that the guest does not support drag'n drop.
-             * This might happen due to not installed Guest Additions or
-             * not running VBoxTray/VBoxClient.
+             * Check if this was a request for getting the next host
+             * message. If so, return the message ID and the parameter
+             * count. The message itself has to be queued.
              */
-            rc = VERR_NOT_SUPPORTED;
-        }
-    }
-    else
-    {
-        /* Tell the host that a wrong drag'n drop mode is set. */
-        rc = VERR_ACCESS_DENIED;
-    }
+            uint32_t uMsgClient = pClient->message();
+
+            uint32_t uMsgNext   = 0;
+            uint32_t cParmsNext = 0;
+            int rcNext = m_pManager->nextMessageInfo(&uMsgNext, &cParmsNext);
+
+            LogFlowFunc(("uMsgClient=%RU32, uMsgNext=%RU32, cParmsNext=%RU32, rcNext=%Rrc\n",
+                         uMsgClient, uMsgNext, cParmsNext, rcNext));
+
+            if (RT_SUCCESS(rcNext))
+            {
+                if (uMsgClient == GUEST_DND_GET_NEXT_HOST_MSG)
+                {
+                    rc = pClient->addMessageInfo(uMsgNext, cParmsNext);
+
+                    /* Note: Report the current rc back to the guest. */
+                    pClient->completeDeferred(rc);
+
+                    m_clientQueue.pop_front();
+                }
+                /*
+                 * Does the message the client is waiting for match the message
+                 * next in the queue? Process it right away then.
+                 */
+                else if (uMsgClient == uMsgNext)
+                {
+                    rc = m_pManager->nextMessage(u32Function, cParms, paParms);
+
+                    /* Note: Report the current rc back to the guest. */
+                    pClient->completeDeferred(rc);
+                }
+                else /* Should not happen. */
+                    AssertMsgFailed(("Client ID=%RU32 in wrong state with uMsg=%RU32 (next message in queue: %RU32)\n",
+                                     pClient->clientId(), uMsgClient, uMsgNext));
+            }
+
+        } /* fSendToGuest */
+
+    } while (0); /* To use breaks. */
 
     LogFlowFuncLeaveRC(rc);
