summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostServices/DragAndDrop
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/HostServices/DragAndDrop')
-rw-r--r--src/VBox/HostServices/DragAndDrop/Makefile.kmk57
-rw-r--r--src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.cpp1219
-rw-r--r--src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.rc51
-rw-r--r--src/VBox/HostServices/DragAndDrop/dndmanager.cpp206
-rw-r--r--src/VBox/HostServices/DragAndDrop/dndmanager.h113
5 files changed, 1646 insertions, 0 deletions
diff --git a/src/VBox/HostServices/DragAndDrop/Makefile.kmk b/src/VBox/HostServices/DragAndDrop/Makefile.kmk
new file mode 100644
index 00000000..2889e61f
--- /dev/null
+++ b/src/VBox/HostServices/DragAndDrop/Makefile.kmk
@@ -0,0 +1,57 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Guest Control Host Service.
+#
+
+#
+# Copyright (C) 2011-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# Include sub-makefile(s).
+# include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk
+
+#
+# The drag and drop service DLL.
+#
+DLLS += VBoxDragAndDropSvc
+VBoxDragAndDropSvc_TEMPLATE = VBOXR3
+VBoxDragAndDropSvc_NAME.os2 = VBoxDnD
+VBoxDragAndDropSvc_DEFS = \
+ VBOX_WITH_HGCM \
+ $(if $(VBOX_WITH_DRAG_AND_DROP_GH),VBOX_WITH_DRAG_AND_DROP_GH,)
+VBoxDragAndDropSvc_INCS = $(PATH_ROOT)/src/VBox/Main/include ./
+VBoxDragAndDropSvc_INCS.win = \
+ $(VBOX_PATH_SDK)
+
+VBoxDragAndDropSvc_SOURCES = \
+ VBoxDragAndDropSvc.cpp \
+ dndmanager.cpp
+
+VBoxDragAndDropSvc_SOURCES += \
+ ../common/client.cpp \
+ ../common/message.cpp
+
+VBoxDragAndDropSvc_SOURCES.win = \
+ VBoxDragAndDropSvc.rc
+
+VBoxDragAndDropSvc_LIBS = \
+ $(LIB_VMM) \
+ $(LIB_RUNTIME) \
+ $(LIB_REM) \
+ $(PATH_STAGE_LIB)/VBoxDnDHostR3Lib$(VBOX_SUFF_LIB)
+
+VBoxDragAndDropSvc_LDFLAGS.darwin = \
+ -install_name $(VBOX_DYLD_EXECUTABLE_PATH)/VBoxDragAndDropSvc.dylib
+
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.cpp b/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.cpp
new file mode 100644
index 00000000..6a46b0f6
--- /dev/null
+++ b/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.cpp
@@ -0,0 +1,1219 @@
+/* $Id: VBoxDragAndDropSvc.cpp $ */
+/** @file
+ * Drag and Drop Service.
+ */
+
+/*
+ * Copyright (C) 2011-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/** @page pg_svc_guest_control Drag and drop HGCM Service
+ *
+ * TODO
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_GUEST_DND
+#include <VBox/GuestHost/DragAndDrop.h>
+#include <VBox/GuestHost/DragAndDropDefs.h>
+#include <VBox/HostServices/Service.h>
+#include <VBox/HostServices/DragAndDropSvc.h>
+
+#include <VBox/err.h>
+
+#include <algorithm>
+#include <list>
+#include <map>
+
+#include "dndmanager.h"
+
+using namespace DragAndDropSvc;
+
+
+/*********************************************************************************************************************************
+* Service class declaration *
+*********************************************************************************************************************************/
+
+class DragAndDropClient : public HGCM::Client
+{
+public:
+
+ DragAndDropClient(uint32_t uClientID)
+ : HGCM::Client(uClientID)
+ {
+ RT_ZERO(m_SvcCtx);
+ }
+
+ virtual ~DragAndDropClient(void)
+ {
+ disconnect();
+ }
+
+public:
+
+ void disconnect(void);
+};
+
+/** 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;
+
+/**
+ * Specialized drag & drop service class.
+ */
+class DragAndDropService : public HGCM::AbstractService<DragAndDropService>
+{
+public:
+
+ explicit DragAndDropService(PVBOXHGCMSVCHELPERS pHelpers)
+ : HGCM::AbstractService<DragAndDropService>(pHelpers)
+ , m_pManager(NULL) {}
+
+protected:
+
+ int init(VBOXHGCMSVCFNTABLE *pTable);
+ int uninit(void);
+ int clientConnect(uint32_t u32ClientID, void *pvClient);
+ int clientDisconnect(uint32_t u32ClientID, void *pvClient);
+ void guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int hostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+
+ int modeSet(uint32_t u32Mode);
+ inline uint32_t modeGet(void) const { return m_u32Mode; };
+
+protected:
+
+ static DECLCALLBACK(int) progressCallback(uint32_t uStatus, uint32_t uPercentage, int rc, void *pvUser);
+
+protected:
+
+ /** 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. The key is the (unique) client ID. */
+ DnDClientQueue m_clientQueue;
+ /** Current drag and drop mode. */
+ uint32_t m_u32Mode;
+};
+
+
+/*********************************************************************************************************************************
+* Client implementation *
+*********************************************************************************************************************************/
+
+/**
+ * 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? */
+ /** @todo Add context ID. */
+
+ 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. */
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Service class implementation *
+*********************************************************************************************************************************/
+
+int DragAndDropService::init(VBOXHGCMSVCFNTABLE *pTable)
+{
+ /* Register functions. */
+ pTable->pfnHostCall = svcHostCall;
+ pTable->pfnSaveState = NULL; /* The service is stateless, so the normal */
+ pTable->pfnLoadState = NULL; /* construction done before restoring suffices */
+ pTable->pfnRegisterExtension = svcRegisterExtension;
+ pTable->pfnNotify = NULL;
+
+ /* Drag'n drop mode is disabled by default. */
+ modeSet(VBOX_DRAG_AND_DROP_MODE_OFF);
+
+ int rc = VINF_SUCCESS;
+
+ try
+ {
+ m_pManager = new DnDManager(&DragAndDropService::progressCallback, this);
+ }
+ catch(std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+int DragAndDropService::uninit(void)
+{
+ LogFlowFuncEnter();
+
+ if (m_pManager)
+ {
+ delete m_pManager;
+ m_pManager = NULL;
+ }
+
+ 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;
+}
+
+int DragAndDropService::clientConnect(uint32_t u32ClientID, void *pvClient)
+{
+ RT_NOREF1(pvClient);
+ if (m_clientMap.size() >= UINT8_MAX) /* Don't allow too much clients at the same time. */
+ {
+ AssertMsgFailed(("Maximum number of clients reached\n"));
+ return VERR_MAX_PROCS_REACHED;
+ }
+
+ int rc = VINF_SUCCESS;
+
+ /*
+ * Add client to our client map.
+ */
+ if (m_clientMap.find(u32ClientID) != m_clientMap.end())
+ rc = VERR_ALREADY_EXISTS;
+
+ if (RT_SUCCESS(rc))
+ {
+ try
+ {
+ DragAndDropClient *pClient = new DragAndDropClient(u32ClientID);
+ pClient->SetSvcContext(m_SvcCtx);
+ m_clientMap[u32ClientID] = pClient;
+ }
+ catch(std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Reset the message queue as soon as a new clients connect
+ * to ensure that every client has the same state.
+ */
+ if (m_pManager)
+ m_pManager->Reset();
+ }
+ }
+
+ LogFlowFunc(("Client %RU32 connected, rc=%Rrc\n", u32ClientID, rc));
+ return rc;
+}
+
+int DragAndDropService::clientDisconnect(uint32_t u32ClientID, void *pvClient)
+{
+ RT_NOREF1(pvClient);
+
+ /* Client not found? Bail out early. */
+ DnDClientMap::iterator itClient = m_clientMap.find(u32ClientID);
+ if (itClient == m_clientMap.end())
+ return VERR_NOT_FOUND;
+
+ /*
+ * Remove from waiters queue.
+ */
+ m_clientQueue.remove(u32ClientID);
+
+ /*
+ * Remove from client map and deallocate.
+ */
+ AssertPtr(itClient->second);
+ delete itClient->second;
+
+ m_clientMap.erase(itClient);
+
+ LogFlowFunc(("Client %RU32 disconnected\n", u32ClientID));
+ return VINF_SUCCESS;
+}
+
+int DragAndDropService::modeSet(uint32_t u32Mode)
+{
+#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)
+ {
+ case VBOX_DRAG_AND_DROP_MODE_OFF:
+ case VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST:
+ case VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST:
+ case VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL:
+ m_u32Mode = u32Mode;
+ break;
+
+ default:
+ m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF;
+ break;
+ }
+
+ return VINF_SUCCESS;
+}
+
+void DragAndDropService::guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
+ void *pvClient, uint32_t u32Function,
+ uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ RT_NOREF1(pvClient);
+ LogFlowFunc(("u32ClientID=%RU32, u32Function=%RU32, cParms=%RU32\n",
+ u32ClientID, u32Function, cParms));
+
+ /* Check if we've the right mode set. */
+ int rc = VERR_ACCESS_DENIED; /* Play safe. */
+ switch (u32Function)
+ {
+ case GUEST_DND_GET_NEXT_HOST_MSG:
+ {
+ if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
+ {
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ LogFlowFunc(("DnD disabled, deferring request\n"));
+ rc = VINF_HGCM_ASYNC_EXECUTE;
+ }
+ break;
+ }
+
+ /* New since protocol v2. */
+ case GUEST_DND_CONNECT:
+ {
+ /*
+ * Never block the initial connect call, as the clients do this when
+ * initializing and might get stuck if drag and drop is set to "disabled" at
+ * that time.
+ */
+ rc = VINF_SUCCESS;
+ break;
+ }
+ case GUEST_DND_HG_ACK_OP:
+ case GUEST_DND_HG_REQ_DATA:
+ case GUEST_DND_HG_EVT_PROGRESS:
+ {
+ if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
+ || modeGet() == VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST)
+ {
+ rc = VINF_SUCCESS;
+ }
+ else
+ LogFlowFunc(("Host -> Guest DnD mode disabled, ignoring request\n"));
+ break;
+ }
+
+ case GUEST_DND_GH_ACK_PENDING:
+ case GUEST_DND_GH_SND_DATA_HDR:
+ case GUEST_DND_GH_SND_DATA:
+ case GUEST_DND_GH_SND_DIR:
+ case GUEST_DND_GH_SND_FILE_HDR:
+ case GUEST_DND_GH_SND_FILE_DATA:
+ case GUEST_DND_GH_EVT_ERROR:
+ {
+#ifdef VBOX_WITH_DRAG_AND_DROP_GH
+ if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
+ || modeGet() == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST)
+ {
+ rc = VINF_SUCCESS;
+ }
+ else
+#endif
+ LogFlowFunc(("Guest -> Host DnD mode disabled, ignoring request\n"));
+ break;
+ }
+
+ default:
+ /* Reach through to DnD manager. */
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+#ifdef DEBUG_andy
+ LogFlowFunc(("Mode (%RU32) check rc=%Rrc\n", modeGet(), rc));
+#endif
+
+#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! */
+ {
+ LogFlowFunc(("Client %RU32: Protocol v%RU32\n", pClient->GetClientID(), pClient->GetProtocolVer()));
+
+ rc = VERR_INVALID_PARAMETER; /* Play safe. */
+
+ switch (u32Function)
+ {
+ /*
+ * Note: Older VBox versions with enabled DnD guest->host support (< 5.0)
+ * used the same message ID (300) for GUEST_DND_GET_NEXT_HOST_MSG and
+ * HOST_DND_GH_REQ_PENDING, which led this service returning
+ * VERR_INVALID_PARAMETER when the guest wanted to actually
+ * handle HOST_DND_GH_REQ_PENDING.
+ */
+ case GUEST_DND_GET_NEXT_HOST_MSG:
+ {
+ LogFlowFunc(("GUEST_DND_GET_NEXT_HOST_MSG\n"));
+ if (cParms == 3)
+ {
+ rc = m_pManager->GetNextMsgInfo(&paParms[0].u.uint32 /* uMsg */, &paParms[1].u.uint32 /* cParms */);
+ if (RT_FAILURE(rc)) /* No queued messages available? */
+ {
+ 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_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data));
+ if (RT_SUCCESS(rc))
+ {
+ paParms[0].u.uint32 = data.uMsg; /* uMsg */
+ paParms[1].u.uint32 = data.cParms; /* cParms */
+ /* Note: paParms[2] was set by the guest as blocking flag. */
+ }
+ }
+ else /* No host callback in place, so drag and drop is not supported by the host. */
+ rc = VERR_NOT_SUPPORTED;
+
+ if (RT_FAILURE(rc))
+ rc = m_pManager->GetNextMsg(u32Function, cParms, paParms);
+
+ /* Some error occurred or no (new) messages available? */
+ if (RT_FAILURE(rc))
+ {
+ uint32_t fFlags = 0;
+ int rc2 = HGCMSvcGetU32(&paParms[2], &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));
+ }
+ }
+ }
+ break;
+ }
+ case GUEST_DND_CONNECT:
+ {
+ LogFlowFunc(("GUEST_DND_CONNECT\n"));
+ if (cParms >= 2)
+ {
+ const uint8_t idxProto = cParms >= 3 ? 1 : 0;
+
+ VBOXDNDCBCONNECTMSGDATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_CONNECT;
+ if (cParms >= 3)
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ else /* Older protocols don't have a context ID. */
+ rc = VINF_SUCCESS;
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[idxProto], &data.uProtocol);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[idxProto + 1], &data.uFlags);
+ if (RT_SUCCESS(rc))
+ pClient->SetProtocolVer(data.uProtocol);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowFunc(("Client %RU32 is now using protocol v%RU32\n", pClient->GetClientID(), pClient->GetProtocolVer()));
+ DO_HOST_CALLBACK();
+ }
+ }
+ break;
+ }
+ case GUEST_DND_HG_ACK_OP:
+ {
+ LogFlowFunc(("GUEST_DND_HG_ACK_OP\n"));
+
+ VBOXDNDCBHGACKOPDATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_HG_ACK_OP;
+
+ switch (pClient->GetProtocolVer())
+ {
+ case 3:
+ {
+ if (cParms == 2)
+ {
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[1], &data.uAction); /* Get drop action. */
+ }
+ break;
+ }
+
+ case 2:
+ default:
+ {
+ if (cParms == 1)
+ rc = HGCMSvcGetU32(&paParms[0], &data.uAction); /* Get drop action. */
+ break;
+ }
+ }
+
+ DO_HOST_CALLBACK();
+ break;
+ }
+ case GUEST_DND_HG_REQ_DATA:
+ {
+ LogFlowFunc(("GUEST_DND_HG_REQ_DATA\n"));
+
+ VBOXDNDCBHGREQDATADATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_HG_REQ_DATA;
+
+ switch (pClient->GetProtocolVer())
+ {
+ case 3:
+ {
+ if (cParms == 3)
+ {
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[1], (void **)&data.pszFormat, &data.cbFormat);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.cbFormat);
+ }
+ break;
+ }
+
+ case 2:
+ default:
+ {
+ if (cParms == 1)
+ rc = HGCMSvcGetPv(&paParms[0], (void**)&data.pszFormat, &data.cbFormat);
+ break;
+ }
+ }
+
+ DO_HOST_CALLBACK();
+ break;
+ }
+ case GUEST_DND_HG_EVT_PROGRESS:
+ {
+ LogFlowFunc(("GUEST_DND_HG_EVT_PROGRESS\n"));
+
+ VBOXDNDCBHGEVTPROGRESSDATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_HG_EVT_PROGRESS;
+
+ switch (pClient->GetProtocolVer())
+ {
+ case 3:
+ {
+ if (cParms == 4)
+ {
+ rc = HGCMSvcGetU32(&paParms[0], &data.uStatus);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[1], &data.uStatus);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.uPercentage);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[3], &data.rc);
+ }
+ break;
+ }
+
+ case 2:
+ default:
+ {
+ if (cParms == 3)
+ {
+ rc = HGCMSvcGetU32(&paParms[0], &data.uStatus);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[1], &data.uPercentage);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.rc);
+ }
+ break;
+ }
+ }
+
+ DO_HOST_CALLBACK();
+ break;
+ }
+#ifdef VBOX_WITH_DRAG_AND_DROP_GH
+ case GUEST_DND_GH_ACK_PENDING:
+ {
+ LogFlowFunc(("GUEST_DND_GH_ACK_PENDING\n"));
+
+ VBOXDNDCBGHACKPENDINGDATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_ACK_PENDING;
+
+ switch (pClient->GetProtocolVer())
+ {
+ case 3:
+ {
+ if (cParms == 5)
+ {
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[1], &data.uDefAction);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.uAllActions);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[3], (void**)&data.pszFormat, &data.cbFormat);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[4], &data.cbFormat);
+ }
+ break;
+ }
+
+ case 2:
+ default:
+ {
+ if (cParms == 3)
+ {
+ rc = HGCMSvcGetU32(&paParms[0], &data.uDefAction);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[1], &data.uAllActions);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[2], (void**)&data.pszFormat, &data.cbFormat);
+ }
+ break;
+ }
+ }
+
+ DO_HOST_CALLBACK();
+ break;
+ }
+ /* New since protocol v3. */
+ case GUEST_DND_GH_SND_DATA_HDR:
+ {
+ LogFlowFunc(("GUEST_DND_GH_SND_DATA_HDR\n"));
+ if (cParms == 12)
+ {
+ VBOXDNDCBSNDDATAHDRDATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA_HDR;
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[1], &data.data.uFlags);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.data.uScreenId);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU64(&paParms[3], &data.data.cbTotal);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[4], &data.data.cbMeta);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[5], &data.data.pvMetaFmt, &data.data.cbMetaFmt);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[6], &data.data.cbMetaFmt);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU64(&paParms[7], &data.data.cObjects);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[8], &data.data.enmCompression);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[9], (uint32_t *)&data.data.enmChecksumType);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[10], &data.data.pvChecksum, &data.data.cbChecksum);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[11], &data.data.cbChecksum);
+
+ LogFlowFunc(("fFlags=0x%x, cbTotalSize=%RU64, cObj=%RU64\n",
+ data.data.uFlags, data.data.cbTotal, data.data.cObjects));
+ DO_HOST_CALLBACK();
+ }
+ break;
+ }
+ case GUEST_DND_GH_SND_DATA:
+ {
+ LogFlowFunc(("GUEST_DND_GH_SND_DATA\n"));
+ switch (pClient->GetProtocolVer())
+ {
+ case 3:
+ {
+ if (cParms == 5)
+ {
+ VBOXDNDCBSNDDATADATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA;
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[1], (void**)&data.data.u.v3.pvData, &data.data.u.v3.cbData);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.data.u.v3.cbData);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[3], (void**)&data.data.u.v3.pvChecksum, &data.data.u.v3.cbChecksum);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[4], &data.data.u.v3.cbChecksum);
+ DO_HOST_CALLBACK();
+ }
+ break;
+ }
+
+ case 2:
+ default:
+ {
+ if (cParms == 2)
+ {
+ VBOXDNDCBSNDDATADATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA;
+ rc = HGCMSvcGetPv(&paParms[0], (void**)&data.data.u.v1.pvData, &data.data.u.v1.cbData);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[1], &data.data.u.v1.cbTotalSize);
+ DO_HOST_CALLBACK();
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case GUEST_DND_GH_SND_DIR:
+ {
+ LogFlowFunc(("GUEST_DND_GH_SND_DIR\n"));
+
+ VBOXDNDCBSNDDIRDATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DIR;
+
+ switch (pClient->GetProtocolVer())
+ {
+ case 3:
+ {
+ if (cParms == 4)
+ {
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[1], (void**)&data.pszPath, &data.cbPath);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.cbPath);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[3], &data.fMode);
+ }
+ break;
+ }
+
+ case 2:
+ default:
+ {
+ if (cParms == 3)
+ {
+ rc = HGCMSvcGetPv(&paParms[0], (void**)&data.pszPath, &data.cbPath);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[1], &data.cbPath);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.fMode);
+ }
+ break;
+ }
+ }
+
+ DO_HOST_CALLBACK();
+ break;
+ }
+ /* New since protocol v2 (>= VBox 5.0). */
+ case GUEST_DND_GH_SND_FILE_HDR:
+ {
+ LogFlowFunc(("GUEST_DND_GH_SND_FILE_HDR\n"));
+ if (cParms == 6)
+ {
+ VBOXDNDCBSNDFILEHDRDATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_HDR;
+
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[1], (void**)&data.pszFilePath, &data.cbFilePath);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.cbFilePath);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[3], &data.fFlags);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[4], &data.fMode);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU64(&paParms[5], &data.cbSize);
+
+ LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x, cbSize=%RU64\n",
+ data.pszFilePath, data.cbFilePath, data.fMode, data.cbSize));
+ DO_HOST_CALLBACK();
+ }
+ break;
+ }
+ case GUEST_DND_GH_SND_FILE_DATA:
+ {
+ LogFlowFunc(("GUEST_DND_GH_SND_FILE_DATA\n"));
+
+ switch (pClient->GetProtocolVer())
+ {
+ /* Protocol v3 adds (optional) checksums. */
+ case 3:
+ {
+ if (cParms == 5)
+ {
+ VBOXDNDCBSNDFILEDATADATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
+
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[1], (void**)&data.pvData, &data.cbData);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.cbData);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[3], (void**)&data.u.v3.pvChecksum, &data.u.v3.cbChecksum);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[4], &data.u.v3.cbChecksum);
+
+ LogFlowFunc(("pvData=0x%p, cbData=%RU32\n", data.pvData, data.cbData));
+ DO_HOST_CALLBACK();
+ }
+ break;
+ }
+ /* Protocol v2 only sends the next data chunks to reduce traffic. */
+ case 2:
+ {
+ if (cParms == 3)
+ {
+ VBOXDNDCBSNDFILEDATADATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[1], (void**)&data.pvData, &data.cbData);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[2], &data.cbData);
+
+ LogFlowFunc(("cbData=%RU32, pvData=0x%p\n", data.cbData, data.pvData));
+ DO_HOST_CALLBACK();
+ }
+ break;
+ }
+ /* Protocol v1 sends the file path and attributes for every file chunk (!). */
+ default:
+ {
+ if (cParms == 5)
+ {
+ VBOXDNDCBSNDFILEDATADATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
+ uint32_t cTmp;
+ rc = HGCMSvcGetPv(&paParms[0], (void**)&data.u.v1.pszFilePath, &cTmp);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[1], &data.u.v1.cbFilePath);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetPv(&paParms[2], (void**)&data.pvData, &cTmp);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[3], &data.cbData);
+ if (RT_SUCCESS(rc))
+ rc = HGCMSvcGetU32(&paParms[4], &data.u.v1.fMode);
+
+ LogFlowFunc(("pszFilePath=%s, cbData=%RU32, pvData=0x%p, fMode=0x%x\n",
+ data.u.v1.pszFilePath, data.cbData, data.pvData, data.u.v1.fMode));
+ DO_HOST_CALLBACK();
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case GUEST_DND_GH_EVT_ERROR:
+ {
+ LogFlowFunc(("GUEST_DND_GH_EVT_ERROR\n"));
+
+ VBOXDNDCBEVTERRORDATA data;
+ RT_ZERO(data);
+ data.hdr.uMagic = CB_MAGIC_DND_GH_EVT_ERROR;
+
+ switch (pClient->GetProtocolVer())
+ {
+ case 3:
+ {
+ if (cParms == 2)
+ {
+ rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t rcOp;
+ rc = HGCMSvcGetU32(&paParms[1], &rcOp);
+ if (RT_SUCCESS(rc))
+ data.rc = rcOp;
+ }
+ }
+ break;
+ }
+
+ case 2:
+ default:
+ {
+ if (cParms == 1)
+ {
+ uint32_t rcOp;
+ rc = HGCMSvcGetU32(&paParms[0], &rcOp);
+ if (RT_SUCCESS(rc))
+ data.rc = (int32_t)rcOp;
+ }
+ break;
+ }
+ }
+
+ DO_HOST_CALLBACK();
+ break;
+ }
+#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
+
+ default:
+ {
+ /* All other messages are handled by the DnD manager. */
+ rc = m_pManager->GetNextMsg(u32Function, cParms, paParms);
+ if (rc == VERR_NO_DATA) /* Manager has no new messsages? Try asking the host. */
+ {
+ if (m_SvcCtx.pfnHostCallback)
+ {
+ VBOXDNDCBHGGETNEXTHOSTMSGDATA data;
+ RT_ZERO(data);
+
+ data.hdr.uMagic = VBOX_DND_CB_MAGIC_MAKE(0 /* uFn */, 0 /* uVer */);
+
+ data.uMsg = u32Function;
+ data.cParms = cParms;
+ data.paParms = paParms;
+
+ rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function,
+ &data, sizeof(data));
+ if (RT_SUCCESS(rc))
+ {
+ 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;
+ }
+ }
+ else /* No host callback in place, so drag and drop is not supported by the host. */
+ rc = VERR_NOT_SUPPORTED;
+ }
+ break;
+ }
+ }
+ }
+
+ /*
+ * If async execution is requested, we didn't notify the guest yet about
+ * completion. The client is queued into the waiters list and will be
+ * notified as soon as a new event is available.
+ */
+ if (rc == VINF_HGCM_ASYNC_EXECUTE)
+ {
+ try
+ {
+ AssertPtr(pClient);
+ pClient->SetDeferred(callHandle, u32Function, cParms, paParms);
+ m_clientQueue.push_back(u32ClientID);
+ }
+ catch (std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ /* Don't report to guest. */
+ }
+ }
+ else if (pClient)
+ pClient->Complete(callHandle, rc);
+ else
+ {
+ AssertMsgFailed(("Guest call failed with %Rrc\n", rc));
+ rc = VERR_NOT_IMPLEMENTED;
+ }
+
+ LogFlowFunc(("Returning rc=%Rrc\n", rc));
+}
+
+int DragAndDropService::hostCall(uint32_t u32Function,
+ uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ LogFlowFunc(("u32Function=%RU32, cParms=%RU32, cClients=%zu, cQueue=%zu\n",
+ u32Function, cParms, m_clientMap.size(), m_clientQueue.size()));
+
+ int rc;
+
+ 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_CANCEL:
+ {
+ LogFlowFunc(("Cancelling all waiting clients ...\n"));
+
+ /* Reset the message queue as the host cancelled the whole operation. */
+ m_pManager->Reset();
+
+ rc = m_pManager->AddMsg(u32Function, cParms, paParms, true /* fAppend */);
+ if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("Adding new message of type=%RU32 failed with rc=%Rrc\n", u32Function, rc));
+ break;
+ }
+
+ /*
+ * 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->SetDeferredMsgInfo(HOST_DND_CANCEL,
+ /* Protocol v3+ also contains the context ID. */
+ pClient->GetProtocolVer() >= 3 ? 1 : 0);
+ pClient->CompleteDeferred(rc2);
+
+ m_clientQueue.erase(itQueue);
+ itQueue = m_clientQueue.begin();
+ }
+
+ Assert(m_clientQueue.empty());
+
+ /* Tell the host that everything went well. */
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ case HOST_DND_HG_EVT_ENTER:
+ {
+ /* Reset the message queue as a new DnD operation just began. */
+ m_pManager->Reset();
+
+ fSendToGuest = true;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ default:
+ {
+ fSendToGuest = true;
+ rc = VINF_SUCCESS;
+ 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.empty()) /* 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->AddMsg(u32Function, cParms, paParms, true /* fAppend */);
+ 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.empty())
+ {
+ 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);
+
+ /*
+ * 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 uMsgClient = pClient->GetMsgType();
+
+ uint32_t uMsgNext = 0;
+ uint32_t cParmsNext = 0;
+ int rcNext = m_pManager->GetNextMsgInfo(&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->SetDeferredMsgInfo(uMsgNext, cParmsNext);
+
+ /* Note: Report the current rc back to the guest. */
+ pClient->CompleteDeferred(rc);
+ }
+ /*
+ * 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->GetNextMsg(u32Function, cParms, paParms);
+
+ /* Note: Report the current rc back to the guest. */
+ pClient->CompleteDeferred(rc);
+ }
+ else /* Should not happen; cancel the operation on the guest. */
+ {
+ LogFunc(("Client ID=%RU32 in wrong state with uMsg=%RU32 (next message in queue: %RU32), cancelling\n",
+ pClient->GetClientID(), uMsgClient, uMsgNext));
+
+ pClient->CompleteDeferred(VERR_CANCELLED);
+ }
+
+ m_clientQueue.pop_front();
+ }
+
+ } /* fSendToGuest */
+
+ } while (0); /* To use breaks. */
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+DECLCALLBACK(int) DragAndDropService::progressCallback(uint32_t uStatus, uint32_t uPercentage, int rc, void *pvUser)
+{
+ AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+
+ DragAndDropService *pSelf = static_cast<DragAndDropService *>(pvUser);
+ AssertPtr(pSelf);
+
+ if (pSelf->m_SvcCtx.pfnHostCallback)
+ {
+ LogFlowFunc(("GUEST_DND_HG_EVT_PROGRESS: uStatus=%RU32, uPercentage=%RU32, rc=%Rrc\n",
+ uStatus, uPercentage, rc));
+
+ VBOXDNDCBHGEVTPROGRESSDATA data;
+ data.hdr.uMagic = CB_MAGIC_DND_HG_EVT_PROGRESS;
+ data.uPercentage = RT_MIN(uPercentage, 100);
+ data.uStatus = uStatus;
+ data.rc = rc; /** @todo uin32_t vs. int. */
+
+ return pSelf->m_SvcCtx.pfnHostCallback(pSelf->m_SvcCtx.pvHostData,
+ GUEST_DND_HG_EVT_PROGRESS,
+ &data, sizeof(data));
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @copydoc VBOXHGCMSVCLOAD
+ */
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
+{
+ return DragAndDropService::svcLoad(pTable);
+}
+
diff --git a/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.rc b/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.rc
new file mode 100644
index 00000000..f4059f39
--- /dev/null
+++ b/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.rc
@@ -0,0 +1,51 @@
+/* $Id: VBoxDragAndDropSvc.rc $ */
+/** @file
+ * VBoxDragAndDropSvc - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2015-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox Drag and Drop Host Service\0"
+ VALUE "InternalName", "VBoxDragAndDropSvc\0"
+ VALUE "OriginalFilename", "VBoxDragAndDropSvc.dll\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/src/VBox/HostServices/DragAndDrop/dndmanager.cpp b/src/VBox/HostServices/DragAndDrop/dndmanager.cpp
new file mode 100644
index 00000000..efc79280
--- /dev/null
+++ b/src/VBox/HostServices/DragAndDrop/dndmanager.cpp
@@ -0,0 +1,206 @@
+/* $Id: dndmanager.cpp $ */
+/** @file
+ * Drag and Drop manager: Handling of DnD messages on the host side.
+ */
+
+/*
+ * Copyright (C) 2011-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+
+#ifdef LOG_GROUP
+ #undef LOG_GROUP
+#endif
+#define LOG_GROUP LOG_GROUP_GUEST_DND
+
+#include "dndmanager.h"
+
+#include <VBox/log.h>
+#include <iprt/file.h>
+#include <iprt/dir.h>
+#include <iprt/path.h>
+#include <iprt/uri.h>
+
+
+/*********************************************************************************************************************************
+* DnDManager *
+*********************************************************************************************************************************/
+
+/**
+ * Adds a DnD message to the manager's queue.
+ *
+ * @returns IPRT status code.
+ * @param pMsg Pointer to DnD message to add. The queue then owns the pointer.
+ * @param fAppend Whether to append or prepend the message to the queue.
+ */
+int DnDManager::AddMsg(DnDMessage *pMsg, bool fAppend /* = true */)
+{
+ AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
+
+ LogFlowFunc(("uMsg=%RU32, cParms=%RU32, fAppend=%RTbool\n", pMsg->GetType(), pMsg->GetParamCount(), fAppend));
+
+ if (fAppend)
+ m_queueMsg.append(pMsg);
+ else
+ m_queueMsg.prepend(pMsg);
+
+ /** @todo Catch / handle OOM? */
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Adds a DnD message to the manager's queue.
+ *
+ * @returns IPRT status code.
+ * @param uMsg Type (function number) of message to add.
+ * @param cParms Number of parameters of message to add.
+ * @param paParms Array of parameters of message to add.
+ * @param fAppend Whether to append or prepend the message to the queue.
+ */
+int DnDManager::AddMsg(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool fAppend /* = true */)
+{
+ int rc;
+
+ try
+ {
+ DnDMessage *pMsg = new DnDGenericMessage(uMsg, cParms, paParms);
+ rc = AddMsg(pMsg, fAppend);
+ }
+ catch(std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Retrieves information about the next message in the queue.
+ *
+ * @returns IPRT status code. VERR_NO_DATA if no next message is available.
+ * @param puType Where to store the message type.
+ * @param pcParms Where to store the message parameter count.
+ */
+int DnDManager::GetNextMsgInfo(uint32_t *puType, uint32_t *pcParms)
+{
+ AssertPtrReturn(puType, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcParms, VERR_INVALID_POINTER);
+
+ int rc;
+
+ if (m_queueMsg.isEmpty())
+ {
+ rc = VERR_NO_DATA;
+ }
+ else
+ {
+ DnDMessage *pMsg = m_queueMsg.first();
+ AssertPtr(pMsg);
+
+ *puType = pMsg->GetType();
+ *pcParms = pMsg->GetParamCount();
+
+ rc = VINF_SUCCESS;
+ }
+
+ LogFlowFunc(("Returning puMsg=%RU32, pcParms=%RU32, rc=%Rrc\n", *puType, *pcParms, rc));
+ return rc;
+}
+
+/**
+ * Retrieves the next queued up message and removes it from the queue on success.
+ * Will return VERR_NO_DATA if no next message is available.
+ *
+ * @returns IPRT status code.
+ * @param uMsg Message type to retrieve.
+ * @param cParms Number of parameters the \@a paParms array can store.
+ * @param paParms Where to store the message parameters.
+ */
+int DnDManager::GetNextMsg(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ LogFlowFunc(("uMsg=%RU32, cParms=%RU32\n", uMsg, cParms));
+
+ /* Check for pending messages in our queue. */
+ if (m_queueMsg.isEmpty())
+ return VERR_NO_DATA;
+
+ /* Get the current message. */
+ DnDMessage *pMsg = m_queueMsg.first();
+ AssertPtr(pMsg);
+
+ m_queueMsg.removeFirst(); /* Remove the current message from the queue. */
+
+ /* Fetch the current message info. */
+ int rc = pMsg->GetData(uMsg, cParms, paParms);
+
+ /*
+ * If there was an error handling the current message or the user has canceled
+ * the operation, we need to cleanup all pending events and inform the progress
+ * callback about our exit.
+ */
+ if (RT_FAILURE(rc))
+ {
+ /* Clear any pending messages. */
+ Reset();
+
+ /* Create a new cancel message to inform the guest + call
+ * the host whether the current transfer was canceled or aborted
+ * due to an error. */
+ try
+ {
+ if (rc == VERR_CANCELLED)
+ LogFlowFunc(("Operation was cancelled\n"));
+
+ DnDHGCancelMessage *pMsgCancel = new DnDHGCancelMessage();
+
+ int rc2 = AddMsg(pMsgCancel, false /* Prepend */);
+ AssertRC(rc2);
+
+ if (m_pfnProgressCallback)
+ {
+ LogFlowFunc(("Notifying host about aborting operation (%Rrc) ...\n", rc));
+ m_pfnProgressCallback( rc == VERR_CANCELLED
+ ? DragAndDropSvc::DND_PROGRESS_CANCELLED
+ : DragAndDropSvc::DND_PROGRESS_ERROR,
+ 100 /* Percent */, rc,
+ m_pvProgressUser);
+ }
+ }
+ catch(std::bad_alloc &)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ LogFlowFunc(("Message processed with rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Resets the manager by clearing the message queue and internal state.
+ */
+void DnDManager::Reset(void)
+{
+ LogFlowFuncEnter();
+
+ while (!m_queueMsg.isEmpty())
+ {
+ delete m_queueMsg.last();
+ m_queueMsg.removeLast();
+ }
+}
+
diff --git a/src/VBox/HostServices/DragAndDrop/dndmanager.h b/src/VBox/HostServices/DragAndDrop/dndmanager.h
new file mode 100644
index 00000000..be5986e6
--- /dev/null
+++ b/src/VBox/HostServices/DragAndDrop/dndmanager.h
@@ -0,0 +1,113 @@
+/** @file
+ * Drag and Drop manager.
+ */
+
+/*
+ * Copyright (C) 2011-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_DragAndDrop_dndmanager_h
+#define VBOX_INCLUDED_SRC_DragAndDrop_dndmanager_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/GuestHost/DragAndDrop.h>
+#include <VBox/HostServices/Service.h>
+#include <VBox/HostServices/DragAndDropSvc.h>
+
+#include <iprt/cpp/ministring.h>
+#include <iprt/cpp/list.h>
+
+typedef DECLCALLBACK(int) FNDNDPROGRESS(uint32_t uState, uint32_t uPercentage, int rc, void *pvUser);
+typedef FNDNDPROGRESS *PFNDNDPROGRESS;
+
+/**
+ * DnD message class. This class forms the base of all other more specialized
+ * message classes.
+ */
+class DnDMessage : public HGCM::Message
+{
+public:
+
+ DnDMessage(void)
+ {
+ }
+
+ DnDMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM aParms[])
+ : Message(uMsg, cParms, aParms) { }
+
+ virtual ~DnDMessage(void) { }
+};
+
+/**
+ * DnD message class for generic messages which didn't need any special
+ * handling.
+ */
+class DnDGenericMessage: public DnDMessage
+{
+public:
+ DnDGenericMessage(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+ : DnDMessage(uMsg, cParms, paParms) { }
+};
+
+/**
+ * DnD message class for informing the guest to cancel any current (and pending) activities.
+ */
+class DnDHGCancelMessage: public DnDMessage
+{
+public:
+
+ DnDHGCancelMessage(void)
+ {
+ int rc2 = initData(DragAndDropSvc::HOST_DND_CANCEL,
+ 0 /* cParms */, 0 /* aParms */);
+ AssertRC(rc2);
+ }
+};
+
+/**
+ * DnD manager. Manage creation and queuing of messages for the various DnD
+ * messages types.
+ */
+class DnDManager
+{
+public:
+
+ DnDManager(PFNDNDPROGRESS pfnProgressCallback, void *pvProgressUser)
+ : m_pfnProgressCallback(pfnProgressCallback)
+ , m_pvProgressUser(pvProgressUser)
+ {}
+
+ virtual ~DnDManager(void)
+ {
+ Reset();
+ }
+
+ int AddMsg(DnDMessage *pMessage, bool fAppend = true);
+ int AddMsg(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool fAppend = true);
+
+ int GetNextMsgInfo(uint32_t *puType, uint32_t *pcParms);
+ int GetNextMsg(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+
+ void Reset(void);
+
+protected:
+
+ /** DnD message queue (FIFO). */
+ RTCList<DnDMessage *> m_queueMsg;
+ /** Pointer to host progress callback. Optional, can be NULL. */
+ PFNDNDPROGRESS m_pfnProgressCallback;
+ /** Pointer to progress callback user context. Can be NULL if not used. */
+ void *m_pvProgressUser;
+};
+#endif /* !VBOX_INCLUDED_SRC_DragAndDrop_dndmanager_h */
+