Index: /trunk/include/VBox/GuestHost/DragAndDrop.h
===================================================================
--- /trunk/include/VBox/GuestHost/DragAndDrop.h	(revision 50507)
+++ /trunk/include/VBox/GuestHost/DragAndDrop.h	(revision 50508)
@@ -1,5 +1,5 @@
 /* $Id$ */
 /** @file
- * DnD: Share functions between host and guest.
+ * DnD: Shared functions between host and guest.
  */
 
@@ -22,4 +22,5 @@
 #include <iprt/cdefs.h>
 #include <iprt/err.h>
+#include <iprt/file.h>
 #include <iprt/types.h>
 
@@ -36,20 +37,55 @@
 int DnDPathSanitize(char *pszPath, size_t cbPath);
 
-typedef struct DnDURIPath
+class DnDURIObject
 {
-    DnDURIPath(const RTCString &strSrcPath,
-               const RTCString &strDstPath,
-               uint32_t fMode, uint64_t cbSize)
-        : m_strSrcPath(strSrcPath)
-        , m_strDstPath(strDstPath)
-        , m_fMode(fMode)
-        , m_cbSize(cbSize) {}
+public:
 
+    enum Type
+    {
+        Unknown = 0,
+        File,
+        Directory
+    };
+
+    DnDURIObject(Type type,
+                 const RTCString &strSrcPath,
+                 const RTCString &strDstPath,
+                 uint32_t fMode, uint64_t cbSize);
+    virtual ~DnDURIObject(void);
+
+public:
+
+    RTCString GetSourcePath(void) const { return m_strSrcPath; }
+    RTCString GetDestPath(void) const { return m_strDstPath; }
+    uint32_t GetMode(void) const { return m_fMode; }
+    uint64_t GetSize(void) const { return m_cbSize; }
+    Type GetType(void) const { return m_Type; }
+
+public:
+
+    bool IsComplete(void) const;
+    static int RebaseURIPath(RTCString &strPath, const RTCString &strBaseOld, const RTCString &strBaseNew);
+    int Read(void *pvBuf, uint32_t cbToRead, uint32_t *pcbRead);
+
+protected:
+
+    void closeInternal(void);
+
+protected:
+
+    Type      m_Type;
     RTCString m_strSrcPath;
     RTCString m_strDstPath;
     uint32_t  m_fMode;
+    /** Size (in bytes) to read/write. */
     uint64_t  m_cbSize;
+    /** Bytes processed reading/writing. */
+    uint64_t  m_cbProcessed;
 
-} DnDURIPath;
+    union
+    {
+        RTFILE m_hFile;
+    } u;
+};
 
 class DnDURIList
@@ -62,11 +98,18 @@
 public:
 
-    int AppendPath(const char *pszPath, uint32_t fFlags);
-    int AppendPathsFromList(const RTCList<RTCString> &lstURI, uint32_t fFlags);
+    int AppendNativePath(const char *pszPath, uint32_t fFlags);
+    int AppendNativePathsFromList(const char *pszNativePaths, size_t cbNativePaths, uint32_t fFlags);
+    int AppendNativePathsFromList(const RTCList<RTCString> &lstNativePaths, uint32_t fFlags);
+    int AppendURIPath(const char *pszURI, uint32_t fFlags);
+    int AppendURIPathsFromList(const char *pszURIPaths, size_t cbURIPaths, uint32_t fFlags);
+    int AppendURIPathsFromList(const RTCList<RTCString> &lstURI, uint32_t fFlags);
+
     void Clear(void);
-    const DnDURIPath &First(void) const { return m_lstTree.first(); }
+    DnDURIObject &First(void) { return m_lstTree.first(); }
     bool IsEmpty(void) { return m_lstTree.isEmpty(); }
     void RemoveFirst(void);
-    RTCString RootToString(void) { return RTCString::join(m_lstRoot, "\r\n") + "\r\n"; }
+    int RootFromURIData(const void *pvData, size_t cbData, uint32_t fFlags);
+    RTCString RootToString(const RTCString &strBasePath = "");
+    size_t RootCount(void) { return m_lstRoot.size(); }
     size_t TotalBytes(void) { return m_cbTotal; }
 
@@ -77,8 +120,8 @@
 protected:
 
-    /** List of top-level (root) URI entries. */
+    /** List of all top-level file/directory entries. */
     RTCList<RTCString>     m_lstRoot;
     /** List of all URI objects added. */
-    RTCList<DnDURIPath>    m_lstTree;
+    RTCList<DnDURIObject>  m_lstTree;
     /** Total size of all URI objects, that is, the file
      *  size of all objects (in bytes). */
Index: /trunk/include/VBox/HostServices/DragAndDropSvc.h
===================================================================
--- /trunk/include/VBox/HostServices/DragAndDropSvc.h	(revision 50507)
+++ /trunk/include/VBox/HostServices/DragAndDropSvc.h	(revision 50508)
@@ -221,5 +221,5 @@
     HGCMFunctionParameter cFormat;      /* OUT uint32_t */
     HGCMFunctionParameter pvData;       /* OUT ptr */
-    HGCMFunctionParameter cData;        /* OUT uint32_t */
+    HGCMFunctionParameter cbData;       /* OUT uint32_t */
 } VBOXDNDHGSENDDATAMSG;
 
@@ -235,5 +235,5 @@
      */
     HGCMFunctionParameter pvData;       /* OUT ptr */
-    HGCMFunctionParameter cData;        /* OUT uint32_t */
+    HGCMFunctionParameter cbData;       /* OUT uint32_t */
 } VBOXDNDHGSENDMOREDATAMSG;
 
@@ -249,5 +249,5 @@
      */
     HGCMFunctionParameter pvName;       /* OUT ptr */
-    HGCMFunctionParameter cName;        /* OUT uint32_t */
+    HGCMFunctionParameter cbName;       /* OUT uint32_t */
     HGCMFunctionParameter fMode;        /* OUT uint32_t */
 } VBOXDNDHGSENDDIRMSG;
@@ -264,7 +264,7 @@
      */
     HGCMFunctionParameter pvName;       /* OUT ptr */
-    HGCMFunctionParameter cName;        /* OUT uint32_t */
+    HGCMFunctionParameter cbName;       /* OUT uint32_t */
     HGCMFunctionParameter pvData;       /* OUT ptr */
-    HGCMFunctionParameter cData;        /* OUT uint32_t */
+    HGCMFunctionParameter cbData;       /* OUT uint32_t */
     HGCMFunctionParameter fMode;        /* OUT uint32_t */
 } VBOXDNDHGSENDFILEMSG;
@@ -316,4 +316,6 @@
     /** Number of parameters the message needs. */
     HGCMFunctionParameter num_parms;    /* OUT uint32_t */
+    /** Whether or not to block (wait) for a
+     *  new message to arrive. */
     HGCMFunctionParameter block;        /* OUT uint32_t */
 
@@ -371,7 +373,42 @@
      * GUEST_DND_GH_SND_DATA
      */
-    HGCMFunctionParameter pData;        /* OUT ptr */
-    HGCMFunctionParameter uSize;        /* OUT uint32_t */
+    HGCMFunctionParameter pvData;       /* OUT ptr */
+    /** Total bytes to send. This can be more than
+     *  the data block specified in pvData above, e.g.
+     *  when sending over file objects. */
+    HGCMFunctionParameter cbTotalBytes; /* OUT uint32_t */
 } VBOXDNDGHSENDDATAMSG;
+
+typedef struct VBOXDNDGHSENDDIRMSG
+{
+    VBoxGuestHGCMCallInfo hdr;
+
+    /**
+     * GH Directory event.
+     *
+     * Used by:
+     * GUEST_DND_HG_SND_DIR
+     */
+    HGCMFunctionParameter pvName;       /* OUT ptr */
+    HGCMFunctionParameter cbName;       /* OUT uint32_t */
+    HGCMFunctionParameter fMode;        /* OUT uint32_t */
+} VBOXDNDGHSENDDIRMSG;
+
+typedef struct VBOXDNDGHSENDFILEMSG
+{
+    VBoxGuestHGCMCallInfo hdr;
+
+    /**
+     * GH File event.
+     *
+     * Used by:
+     * GUEST_DND_HG_SND_FILE
+     */
+    HGCMFunctionParameter pvName;       /* OUT ptr */
+    HGCMFunctionParameter cbName;       /* OUT uint32_t */
+    HGCMFunctionParameter pvData;       /* OUT ptr */
+    HGCMFunctionParameter cbData;       /* OUT uint32_t */
+    HGCMFunctionParameter fMode;        /* OUT uint32_t */
+} VBOXDNDGHSENDFILEMSG;
 
 typedef struct VBOXDNDGHEVTERRORMSG
@@ -391,5 +428,5 @@
 
 /*
- * Callback handler
+ * Callback data magics.
  */
 enum
@@ -400,4 +437,6 @@
     CB_MAGIC_DND_GH_ACK_PENDING  = 0xbe975a14,
     CB_MAGIC_DND_GH_SND_DATA     = 0x4eb61bff,
+    CB_MAGIC_DND_GH_SND_DIR      = 0x411ca754,
+    CB_MAGIC_DND_GH_SND_FILE     = 0x65e35eaf,
     CB_MAGIC_DND_GH_EVT_ERROR    = 0x117a87c4
 };
@@ -454,7 +493,31 @@
     void     *pvData;
     uint32_t  cbData;
-    uint32_t  cbAllSize; /** @todo Why is this transmitted every time? */
+    /** Total metadata size (in bytes). This is transmitted
+     *  with every message because the size can change. */
+    uint32_t  cbTotalSize;
 } VBOXDNDCBSNDDATADATA;
 typedef VBOXDNDCBSNDDATADATA *PVBOXDNDCBSNDDATADATA;
+
+typedef struct VBOXDNDCBSNDDIRDATA
+{
+    /** Callback data header. */
+    VBOXDNDCBHEADERDATA hdr;
+    char     *pszPath;
+    uint32_t  cbPath;
+    uint32_t  fMode;
+} VBOXDNDCBSNDDIRDATA;
+typedef VBOXDNDCBSNDDIRDATA *PVBOXDNDCBSNDDIRDATA;
+
+typedef struct VBOXDNDCBSNDFILEDATA
+{
+    /** Callback data header. */
+    VBOXDNDCBHEADERDATA hdr;
+    char     *pszFilePath;
+    uint32_t  cbFilePath;
+    uint32_t  fMode;
+    void     *pvData;
+    uint32_t  cbData;
+} VBOXDNDCBSNDFILEDATA;
+typedef VBOXDNDCBSNDFILEDATA *PVBOXDNDCBSNDFILEDATA;
 
 typedef struct VBOXDNDCBEVTERRORDATA
Index: /trunk/include/VBox/VBoxGuestLib.h
===================================================================
--- /trunk/include/VBox/VBoxGuestLib.h	(revision 50507)
+++ /trunk/include/VBox/VBoxGuestLib.h	(revision 50508)
@@ -723,5 +723,5 @@
 #  ifdef VBOX_WITH_DRAG_AND_DROP_GH
 VBGLR3DECL(int)     VbglR3DnDGHAcknowledgePending(uint32_t u32ClientId, uint32_t uDefAction, uint32_t uAllActions, const char* pcszFormats);
-VBGLR3DECL(int)     VbglR3DnDGHSendData(uint32_t u32ClientId, void *pvData, uint32_t cbData);
+VBGLR3DECL(int)     VbglR3DnDGHSendData(uint32_t u32ClientId, const char *pszFormat, void *pvData, uint32_t cbData);
 VBGLR3DECL(int)     VbglR3DnDGHErrorEvent(uint32_t u32ClientId, int rcOp);
 #  endif /* VBOX_WITH_DRAG_AND_DROP_GH */
Index: /trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDnD.cpp
===================================================================
--- /trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDnD.cpp	(revision 50507)
+++ /trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDnD.cpp	(revision 50508)
@@ -49,7 +49,9 @@
  *  at NT4 SP3+. */
 typedef BOOL (WINAPI *PFNSENDINPUT)(UINT, LPINPUT, int);
+typedef BOOL (WINAPI* PFNENUMDISPLAYMONITORS)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
 
 /** Static pointer to SendInput() function. */
 static PFNSENDINPUT s_pfnSendInput = NULL;
+static PFNENUMDISPLAYMONITORS s_pfnEnumDisplayMonitors = NULL;
 
 static LRESULT CALLBACK vboxDnDWndProcInstance(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
@@ -58,5 +60,4 @@
 VBoxDnDWnd::VBoxDnDWnd(void)
     : hWnd(NULL),
-      mpfnEnumDisplayMonitors(NULL),
       mfMouseButtonDown(false),
 #ifdef VBOX_WITH_DRAG_AND_DROP_GH
@@ -85,11 +86,4 @@
     /* Save the context. */
     this->pContext = pContext;
-
-    HMODULE hUser = GetModuleHandle("user32.dll");
-    if (hUser)
-    {
-        *(uintptr_t *)&mpfnEnumDisplayMonitors = (uintptr_t)GetProcAddress(hUser, "EnumDisplayMonitors");
-        Log(("DnD: EnumDisplayMonitors = %p\n", mpfnEnumDisplayMonitors));
-    }
 
     int rc = RTSemEventCreate(&mEventSem);
@@ -1014,6 +1008,6 @@
     AssertReturn(cbFormats, VERR_INVALID_PARAMETER);
 
-    LogFlowThisFunc(("mMode=%ld, mState=%ld, pDropTarget=0x%p, uDefAction=0x%x\n",
-                     mMode, mState, pDropTarget, uDefAction));
+    LogFlowThisFunc(("mMode=%ld, mState=%ld, pDropTarget=0x%p, pszFormat=%s, uDefAction=0x%x\n",
+                     mMode, mState, pDropTarget, pszFormat, uDefAction));
 
     int rc;
@@ -1025,6 +1019,4 @@
         {
             /** @todo Respect uDefAction. */
-            /** @todo Do data checking / conversion based on pszFormats. */
-
             void *pvData = pDropTarget->DataMutableRaw();
             AssertPtr(pvData);
@@ -1032,5 +1024,5 @@
             Assert(cbData);
 
-            rc = VbglR3DnDGHSendData(mClientID, pvData, cbData);
+            rc = VbglR3DnDGHSendData(mClientID, pszFormat, pvData, cbData);
             LogFlowFunc(("Sent pvData=0x%p, cbData=%RU32, rc=%Rrc\n",
                          pvData, cbData, rc));
@@ -1094,7 +1086,8 @@
     if (hDC)
     {
-        fRc = mpfnEnumDisplayMonitors?
-                  mpfnEnumDisplayMonitors(hDC, NULL, VBoxDnDWnd::MonitorEnumProc, (LPARAM)&r):
-                  FALSE;
+        fRc = s_pfnEnumDisplayMonitors
+            /* EnumDisplayMonitors is not available on NT4. */
+            ? s_pfnEnumDisplayMonitors(hDC, NULL, VBoxDnDWnd::MonitorEnumProc, (LPARAM)&r):
+              FALSE;
 
         if (!fRc)
@@ -1286,4 +1279,7 @@
         RTLdrGetSystemSymbol("User32.dll", "SendInput");
     fSupportedOS = !RT_BOOL(s_pfnSendInput == NULL);
+    s_pfnEnumDisplayMonitors = (PFNENUMDISPLAYMONITORS)
+        RTLdrGetSystemSymbol("User32.dll", "EnumDisplayMonitors");
+    /* g_pfnEnumDisplayMonitors is optional. */
 
     if (!fSupportedOS)
Index: /trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDnD.h
===================================================================
--- /trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDnD.h	(revision 50507)
+++ /trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDnD.h	(revision 50508)
@@ -366,5 +366,4 @@
     /** The window's handle. */
     HWND                       hWnd;
-    BOOL (WINAPI* mpfnEnumDisplayMonitors)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
     /** List of allowed MIME types this
      *  client can handle. Make this a per-instance
Index: /trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDnDDropTarget.cpp
===================================================================
--- /trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDnDDropTarget.cpp	(revision 50507)
+++ /trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDnDDropTarget.cpp	(revision 50508)
@@ -356,13 +356,4 @@
                                                      0 /* cchFile */);
                             Assert(cch);
-
-                            /* Add separation between filenames. */
-                            if (i > 0)
-                            {
-                                rc = RTStrAAppendExN(&pszFiles, 1 /* cPairs */,
-                                                     "\r\n", 2 /* Bytes */);
-                                if (RT_SUCCESS(rc))
-                                    cchFiles += 2; /* Include \r\n */
-                            }
 
                             if (RT_FAILURE(rc))
@@ -418,4 +409,14 @@
                             if (RT_FAILURE(rc))
                                 break;
+
+                            /* Add separation between filenames.
+                             * Note: Also do this for the last element of the list. */
+                            if (i > 0)
+                            {
+                                rc = RTStrAAppendExN(&pszFiles, 1 /* cPairs */,
+                                                     "\r\n", 2 /* Bytes */);
+                                if (RT_SUCCESS(rc))
+                                    cchFiles += 2; /* Include \r\n */
+                            }
                         }
 
Index: /trunk/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibDragAndDrop.cpp
===================================================================
--- /trunk/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibDragAndDrop.cpp	(revision 50507)
+++ /trunk/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibDragAndDrop.cpp	(revision 50508)
@@ -38,4 +38,10 @@
 #include <iprt/cpp/ministring.h>
 
+#ifdef LOG_GROUP
+ #undef LOG_GROUP
+#endif
+#define LOG_GROUP LOG_GROUP_GUEST_DND
+#include <VBox/log.h>
+
 #include <VBox/GuestHost/DragAndDrop.h>
 #include <VBox/HostServices/DragAndDropSvc.h>
@@ -79,5 +85,4 @@
         if (RT_SUCCESS(rc))
         {
-            /* Fetch results */
             rc = Msg.msg.GetUInt32(puMsg);         AssertRC(rc);
             rc = Msg.num_parms.GetUInt32(pcParms); AssertRC(rc);
@@ -128,5 +133,4 @@
         if (RT_SUCCESS(rc))
         {
-            /* Fetch results */
             rc = Msg.uScreenId.GetUInt32(puScreenId);     AssertRC(rc);
             rc = Msg.uX.GetUInt32(puX);                   AssertRC(rc);
@@ -135,5 +139,5 @@
             rc = Msg.uAllActions.GetUInt32(puAllActions); AssertRC(rc);
             rc = Msg.cFormats.GetUInt32(pcbFormatsRecv);  AssertRC(rc);
-            /* A little bit paranoia */
+
             AssertReturn(cbFormats >= *pcbFormatsRecv, VERR_TOO_MUCH_DATA);
         }
@@ -191,5 +195,5 @@
 
     Msg.pvName.SetPtr(pszDirname, cbDirname);
-    Msg.cName.SetUInt32(0);
+    Msg.cbName.SetUInt32(0);
     Msg.fMode.SetUInt32(0);
 
@@ -200,8 +204,7 @@
         if (RT_SUCCESS(Msg.hdr.result))
         {
-            /* Fetch results */
-            rc = Msg.cName.GetUInt32(pcbDirnameRecv); AssertRC(rc);
-            rc = Msg.fMode.GetUInt32(pfMode);         AssertRC(rc);
-            /* A little bit paranoia */
+            rc = Msg.cbName.GetUInt32(pcbDirnameRecv); AssertRC(rc);
+            rc = Msg.fMode.GetUInt32(pfMode);          AssertRC(rc);
+
             AssertReturn(cbDirname >= *pcbDirnameRecv, VERR_TOO_MUCH_DATA);
         }
@@ -235,7 +238,7 @@
 
     Msg.pvName.SetPtr(pszFilename, cbFilename);
-    Msg.cName.SetUInt32(0);
+    Msg.cbName.SetUInt32(0);
     Msg.pvData.SetPtr(pvData, cbData);
-    Msg.cData.SetUInt32(0);
+    Msg.cbData.SetUInt32(0);
     Msg.fMode.SetUInt32(0);
 
@@ -246,9 +249,8 @@
         if (RT_SUCCESS(rc))
         {
-            /* Fetch results */
-            rc = Msg.cName.GetUInt32(pcbFilenameRecv); AssertRC(rc);
-            rc = Msg.cData.GetUInt32(pcbDataRecv);     AssertRC(rc);
-            rc = Msg.fMode.GetUInt32(pfMode);          AssertRC(rc);
-            /* A little bit paranoia */
+            rc = Msg.cbName.GetUInt32(pcbFilenameRecv); AssertRC(rc);
+            rc = Msg.cbData.GetUInt32(pcbDataRecv);     AssertRC(rc);
+            rc = Msg.fMode.GetUInt32(pfMode);           AssertRC(rc);
+
             AssertReturn(cbFilename >= *pcbFilenameRecv, VERR_TOO_MUCH_DATA);
             AssertReturn(cbData     >= *pcbDataRecv,     VERR_TOO_MUCH_DATA);
@@ -275,10 +277,5 @@
         return VERR_INVALID_PARAMETER;
 
-    /* Make a string list out of the uri data. */
-    RTCList<RTCString> uriList =
-        RTCString(static_cast<char*>(*ppvData), *pcbDataRecv - 1).split("\r\n");
-    if (uriList.isEmpty())
-        return VINF_SUCCESS;
-
+    /* Allocate temp buffer. */
     uint32_t cbTmpData = _1M * 10; /** @todo r=andy 10MB, uh, really?? */
     void *pvTmpData = RTMemAlloc(cbTmpData);
@@ -297,39 +294,15 @@
     /* Patch the old drop data with the new drop directory, so the drop target
      * can find the files. */
-    RTCList<RTCString> guestUriList;
-    for (size_t i = 0; i < uriList.size(); ++i)
-    {
-        const RTCString &strUri = uriList.at(i);
-        /* Query the path component of a file URI. If this hasn't a
-         * file scheme, null is returned. */
-        char *pszFilePath = RTUriFilePath(strUri.c_str(), URI_FILE_FORMAT_AUTO);
-        if (pszFilePath)
-        {
-            rc = DnDPathSanitize(pszFilePath, strlen(pszFilePath));
-            if (RT_FAILURE(rc))
-                break;
-
-            /** @todo Use RTPathJoin? */
-            RTCString strFullPath
-                = RTCString().printf("%s%c%s", pszDropDir, RTPATH_SLASH, pszFilePath);
-            char *pszNewUri = RTUriFileCreate(strFullPath.c_str());
-            if (pszNewUri)
-            {
-                guestUriList.append(pszNewUri);
-                RTStrFree(pszNewUri);
-            }
-        }
-        else
-            guestUriList.append(strUri);
-    }
-
+    DnDURIList lstURI;
+    rc = lstURI.RootFromURIData(*ppvData, *pcbDataRecv,
+                                0 /* fFlags */);
     if (RT_SUCCESS(rc))
     {
         /* Cleanup the old data and write the new data back to the event. */
         RTMemFree(*ppvData);
-        RTCString newData = RTCString::join(guestUriList, "\r\n") + "\r\n";
-
-        *ppvData = RTStrDupN(newData.c_str(), newData.length());
-        *pcbDataRecv = newData.length() + 1;
+
+        RTCString strData = lstURI.RootToString(pszDropDir);
+        *ppvData = RTStrDupN(strData.c_str(), strData.length());
+        *pcbDataRecv = strData.length() + 1;
     }
 
@@ -403,4 +376,5 @@
                             if (RT_SUCCESS(rc))
                             {
+                                /** @todo r=andy Not very safe to assume that we were last appending to the current file. */
                                 rc = RTFileSeek(hFile, 0, RTFILE_SEEK_END, NULL);
                                 if (RT_SUCCESS(rc))
@@ -488,5 +462,5 @@
     Msg.cFormat.SetUInt32(0);
     Msg.pvData.SetPtr(pvData, cbData);
-    Msg.cData.SetUInt32(0);
+    Msg.cbData.SetUInt32(0);
 
     int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
@@ -497,9 +471,8 @@
             || rc == VERR_BUFFER_OVERFLOW)
         {
-            /* Fetch results */
             rc = Msg.uScreenId.GetUInt32(puScreenId);  AssertRC(rc);
             rc = Msg.cFormat.GetUInt32(pcbFormatRecv); AssertRC(rc);
-            rc = Msg.cData.GetUInt32(pcbDataTotal);     AssertRC(rc);
-            /* A little bit paranoia */
+            rc = Msg.cbData.GetUInt32(pcbDataTotal);     AssertRC(rc);
+
             AssertReturn(cbFormat >= *pcbFormatRecv, VERR_TOO_MUCH_DATA);
             AssertReturn(cbData   >= *pcbDataTotal,  VERR_TOO_MUCH_DATA);
@@ -526,5 +499,5 @@
 
     Msg.pvData.SetPtr(pvData, cbData);
-    Msg.cData.SetUInt32(0);
+    Msg.cbData.SetUInt32(0);
 
     int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
@@ -535,7 +508,5 @@
             || rc == VERR_BUFFER_OVERFLOW)
         {
-            /* Fetch results */
-            rc = Msg.cData.GetUInt32(pcbDataRecv); AssertRC(rc);
-            /* A little bit paranoia */
+            rc = Msg.cbData.GetUInt32(pcbDataRecv); AssertRC(rc);
             AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
         }
@@ -629,5 +600,6 @@
          * This keeps the actual (guest OS-)dependent client (like VBoxClient /
          * VBoxTray) small by not having too much redundant code. */
-        if (RTStrNICmp(pszFormat, "text/uri-list", *pcbFormatRecv) == 0)
+        AssertPtr(pcbFormatRecv);
+        if (DnDMIMEHasFileURLs(pszFormat, *pcbFormatRecv))
             rc = vbglR3DnDHGProcessURIMessages(uClientId,
                                                puScreenId,
@@ -661,8 +633,5 @@
         rc = Msg.hdr.result;
         if (RT_SUCCESS(rc))
-        {
-            /* Fetch results */
             rc = Msg.uScreenId.GetUInt32(puScreenId); AssertRC(rc);
-        }
     }
 
@@ -697,8 +666,7 @@
         if (RT_SUCCESS(rc))
         {
-            /* Fetch results */
             rc = Msg.cFormat.GetUInt32(pcbFormatRecv); AssertRC(rc);
             rc = Msg.uAction.GetUInt32(puAction);      AssertRC(rc);
-            /* A little bit paranoia */
+
             AssertReturn(cbFormat >= *pcbFormatRecv, VERR_TOO_MUCH_DATA);
         }
@@ -872,7 +840,7 @@
     Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_HG_ACK_OP;
     Msg.hdr.cParms      = 1;
-    /* Initialize parameter */
+
     Msg.uAction.SetUInt32(uAction);
-    /* Do request */
+
     int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
     if (RT_SUCCESS(rc))
@@ -892,7 +860,7 @@
     Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_HG_REQ_DATA;
     Msg.hdr.cParms      = 1;
-    /* Initialize parameter */
+
     Msg.pFormat.SetPtr((void*)pcszFormat, (uint32_t)strlen(pcszFormat) + 1);
-    /* Do request */
+
     int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
     if (RT_SUCCESS(rc))
@@ -914,21 +882,22 @@
     Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_GH_ACK_PENDING;
     Msg.hdr.cParms      = 3;
-    /* Initialize parameter */
+
     Msg.uDefAction.SetUInt32(uDefAction);
     Msg.uAllActions.SetUInt32(uAllActions);
     Msg.pFormat.SetPtr((void*)pcszFormats, (uint32_t)strlen(pcszFormats) + 1);
-    /* Do request */
-    int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
-    if (RT_SUCCESS(rc))
-        rc = Msg.hdr.result;
-
-    return rc;
-}
-
-VBGLR3DECL(int) VbglR3DnDGHSendData(uint32_t u32ClientId,
-                                    void *pvData, uint32_t cbData)
+
+    int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
+    if (RT_SUCCESS(rc))
+        rc = Msg.hdr.result;
+
+    return rc;
+}
+
+static int vbglR3DnDGHSendDataInternal(uint32_t u32ClientId,
+                                       void *pvData, uint32_t cbData,
+                                       uint32_t cbAdditionalData)
 {
     AssertPtrReturn(pvData, VERR_INVALID_POINTER);
-    AssertReturn(cbData,    VERR_INVALID_PARAMETER);
+    AssertReturn(cbData, VERR_INVALID_PARAMETER);
 
     DragAndDropSvc::VBOXDNDGHSENDDATAMSG Msg;
@@ -938,14 +907,17 @@
     Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_GH_SND_DATA;
     Msg.hdr.cParms      = 2;
-    Msg.uSize.SetUInt32(cbData);
-
-    int rc          = VINF_SUCCESS;
-    uint32_t cbMax  = _64K; /* Transfer max. 64K chunks per message. */
-    uint32_t cbSent = 0;
+
+    /* Total amount of bytes to send (including this data block). */
+    Msg.cbTotalBytes.SetUInt32(cbData + cbAdditionalData);
+
+    int rc;
+
+    uint32_t cbMaxChunk = _64K; /* Transfer max. 64K chunks per message. */
+    uint32_t cbSent     = 0;
 
     while (cbSent < cbData)
     {
-        uint32_t cbToSend = RT_MIN(cbData - cbSent, cbMax);
-        Msg.pData.SetPtr(static_cast<uint8_t*>(pvData) + cbSent, cbToSend);
+        uint32_t cbCurChunk = RT_MIN(cbData - cbSent, cbMaxChunk);
+        Msg.pvData.SetPtr(static_cast<uint8_t*>(pvData) + cbSent, cbCurChunk);
 
         rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
@@ -956,9 +928,183 @@
             break;
 
-        cbSent += cbToSend;
+        cbSent += cbCurChunk;
     }
 
     if (RT_SUCCESS(rc))
         Assert(cbSent == cbData);
+
+    LogFlowFunc(("Returning rc=%Rrc, cbData=%RU32, cbAddtionalData=%RU32\n",
+                 rc, cbData, cbAdditionalData));
+    return rc;
+}
+
+static int vbglR3DnDGHSendDir(uint32_t u32ClientId, DnDURIObject &obj)
+{
+    AssertReturn(obj.GetType() == DnDURIObject::Directory, VERR_INVALID_PARAMETER);
+
+    DragAndDropSvc::VBOXDNDGHSENDDIRMSG Msg;
+    RT_ZERO(Msg);
+    Msg.hdr.result      = VERR_WRONG_ORDER;
+    Msg.hdr.u32ClientID = u32ClientId;
+    Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_GH_SND_DIR;
+    Msg.hdr.cParms      = 3;
+
+    RTCString strPath = obj.GetDestPath();
+    LogFlowFunc(("strDir=%s (%zu), fMode=0x%x\n",
+                 strPath.c_str(), strPath.length(), obj.GetMode()));
+
+    Msg.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)(strPath.length() + 1));
+    Msg.cbName.SetUInt32((uint32_t)(strPath.length() + 1));
+    Msg.fMode.SetUInt32(obj.GetMode());
+
+    int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
+    if (RT_SUCCESS(rc))
+        rc = Msg.hdr.result;
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static int vbglR3DnDGHSendFile(uint32_t u32ClientId, DnDURIObject &obj)
+{
+    AssertReturn(obj.GetType() == DnDURIObject::File, VERR_INVALID_PARAMETER);
+
+    uint32_t cbBuf = _64K; /** @todo Make this configurable? */
+    void *pvBuf = RTMemAlloc(cbBuf);
+    if (!pvBuf)
+        return VERR_NO_MEMORY;
+
+    DragAndDropSvc::VBOXDNDGHSENDFILEMSG Msg;
+    RT_ZERO(Msg);
+    Msg.hdr.result      = VERR_WRONG_ORDER;
+    Msg.hdr.u32ClientID = u32ClientId;
+    Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_GH_SND_FILE;
+    Msg.hdr.cParms      = 5;
+
+    RTCString strPath = obj.GetDestPath();
+    LogFlowFunc(("strFile=%s (%zu), fMode=0x%x\n",
+                 strPath.c_str(), strPath.length(), obj.GetMode()));
+
+    Msg.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)(strPath.length() + 1));
+    Msg.cbName.SetUInt32((uint32_t)(strPath.length() + 1));
+    Msg.fMode.SetUInt32(obj.GetMode());
+
+    int rc = VINF_SUCCESS;
+    uint32_t cbData = obj.GetSize();
+
+    do
+    {
+        uint32_t cbToRead = RT_MIN(cbData, cbBuf);
+        uint32_t cbRead   = 0;
+        if (cbToRead)
+            rc = obj.Read(pvBuf, cbToRead, &cbRead);
+        if (RT_SUCCESS(rc))
+        {
+             Msg.cbData.SetUInt32(cbRead);
+             Msg.pvData.SetPtr(pvBuf, cbRead);
+
+             rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
+             if (RT_SUCCESS(rc))
+                 rc = Msg.hdr.result;
+        }
+
+        if (RT_FAILURE(rc))
+            break;
+
+        Assert(cbRead <= cbData);
+        cbData -= cbRead;
+
+    } while (cbData);
+
+    RTMemFree(pvBuf);
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+static int vbglR3DnDGHSendURIObject(uint32_t u32ClientId, DnDURIObject &obj)
+{
+    int rc;
+
+    switch (obj.GetType())
+    {
+        case DnDURIObject::Directory:
+            rc = vbglR3DnDGHSendDir(u32ClientId, obj);
+            break;
+
+        case DnDURIObject::File:
+            rc = vbglR3DnDGHSendFile(u32ClientId, obj);
+            break;
+
+        default:
+            AssertMsgFailed(("Type %ld not implemented\n",
+                             obj.GetType()));
+            rc = VERR_NOT_IMPLEMENTED;
+            break;
+    }
+
+    return rc;
+}
+
+static int vbglR3DnDGHProcessURIMessages(uint32_t u32ClientId,
+                                         void *pvData, uint32_t cbData)
+{
+    AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+    AssertReturn(cbData, VERR_INVALID_PARAMETER);
+
+    RTCList<RTCString> lstPaths =
+        RTCString((const char *)pvData, cbData).split("\r\n");
+
+    DnDURIList lstURI;
+    int rc = lstURI.AppendNativePathsFromList(lstPaths, 0 /* fFlags */);
+    if (RT_SUCCESS(rc))
+    {
+        /* Send metadata; in this case it's the (non-recursive) file/directory
+         * URI list the host needs to know to initialize the drag'n drop operation. */
+        RTCString strRootDest = lstURI.RootToString();
+        Assert(strRootDest.isNotEmpty());
+
+        void *pvToSend = (void *)strRootDest.c_str();
+        uint32_t cbToSend = (uint32_t)strRootDest.length() + 1;
+
+        rc = vbglR3DnDGHSendDataInternal(u32ClientId, pvToSend, cbToSend,
+                                         /* Include total bytes of all file paths,
+                                          * file sizes etc. */
+                                         lstURI.TotalBytes());
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        while (!lstURI.IsEmpty())
+        {
+            DnDURIObject &nextObj = lstURI.First();
+
+            rc = vbglR3DnDGHSendURIObject(u32ClientId, nextObj);
+            if (RT_FAILURE(rc))
+                break;
+
+            lstURI.RemoveFirst();
+        }
+    }
+
+    return rc;
+}
+
+VBGLR3DECL(int) VbglR3DnDGHSendData(uint32_t u32ClientId,
+                                    const char *pszFormat,
+                                    void *pvData, uint32_t cbData)
+{
+    AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+    AssertReturn(cbData, VERR_INVALID_PARAMETER);
+
+    int rc;
+    if (DnDMIMEHasFileURLs(pszFormat, strlen(pszFormat)))
+    {
+        rc = vbglR3DnDGHProcessURIMessages(u32ClientId, pvData, cbData);
+    }
+    else
+        rc = vbglR3DnDGHSendDataInternal(u32ClientId, pvData, cbData,
+                                         0 /* cbAdditionalData */);
 
     return rc;
@@ -973,17 +1119,12 @@
     Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_GH_EVT_ERROR;
     Msg.hdr.cParms      = 1;
-    /* Initialize parameter */
+
     Msg.uRC.SetUInt32(rcOp);
-    /* Do request */
-    int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
-    if (RT_SUCCESS(rc))
-        rc = Msg.hdr.result;
-
-    return rc;
-}
-
-VBGLR3DECL(int) VbglR3DnDGHSendFile(uint32_t u32ClientId, const char *pszPath)
-{
-    return 0;
-}
-
+
+    int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
+    if (RT_SUCCESS(rc))
+        rc = Msg.hdr.result;
+
+    return rc;
+}
+
Index: /trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDDataObject_win.cpp
===================================================================
--- /trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDDataObject_win.cpp	(revision 50507)
+++ /trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDDataObject_win.cpp	(revision 50508)
@@ -53,13 +53,13 @@
     HRESULT hr;
 
-    ULONG cAllFormats = lstFormats.size();
+    ULONG cMaxFormats = 16; /* Maximum number of registered formats. */
     ULONG cRegisteredFormats = 0;
 
     try
     {
-        mpFormatEtc = new FORMATETC[cAllFormats];
-        RT_BZERO(mpFormatEtc, sizeof(FORMATETC) * cAllFormats);
-        mpStgMedium = new STGMEDIUM[cAllFormats];
-        RT_BZERO(mpStgMedium, sizeof(STGMEDIUM) * cAllFormats);
+        mpFormatEtc = new FORMATETC[cMaxFormats];
+        RT_BZERO(mpFormatEtc, sizeof(FORMATETC) * cMaxFormats);
+        mpStgMedium = new STGMEDIUM[cMaxFormats];
+        RT_BZERO(mpStgMedium, sizeof(STGMEDIUM) * cMaxFormats);
 
         for (int i = 0; i < lstFormats.size(); i++)
@@ -69,7 +69,11 @@
                 continue;
 
+            /* URI data ("text/uri-list"). */
             if (strFormat.contains("text/uri-list", Qt::CaseInsensitive))
             {
-                /* URI data ("text/uri-list"). */
+                RegisterFormat(&mpFormatEtc[cRegisteredFormats], CF_TEXT);
+                mpStgMedium[cRegisteredFormats++].tymed = TYMED_HGLOBAL;
+                RegisterFormat(&mpFormatEtc[cRegisteredFormats], CF_UNICODETEXT);
+                mpStgMedium[cRegisteredFormats++].tymed = TYMED_HGLOBAL;
                 RegisterFormat(&mpFormatEtc[cRegisteredFormats], CF_HDROP);
                 mpStgMedium[cRegisteredFormats++].tymed = TYMED_HGLOBAL;
@@ -77,9 +81,11 @@
                 mlstFormats << strFormat;
             }
+            /* Plain text ("text/plain"). */
             else if (strFormat.contains("text/plain", Qt::CaseInsensitive))
             {
-                /* Plain text ("text/plain"). */
                 RegisterFormat(&mpFormatEtc[cRegisteredFormats], CF_TEXT);
                 mpStgMedium[cRegisteredFormats++].tymed = TYMED_HGLOBAL;
+                RegisterFormat(&mpFormatEtc[cRegisteredFormats], CF_UNICODETEXT);
+                mpStgMedium[cRegisteredFormats++].tymed = TYMED_HGLOBAL;
 
                 mlstFormats << strFormat;
@@ -89,8 +95,4 @@
         LogFlowFunc(("Total registered formats: %RU32 (of %d total)\n",
                      cRegisteredFormats, lstFormats.size()));
-#ifdef VBOX_STRICT
-        AssertMsg(cRegisteredFormats == cAllFormats,
-                  ("Registered formats count is not matching all formats count\n"));
-#endif
         hr = S_OK;
     }
@@ -201,5 +203,7 @@
     AssertPtrReturn(pMedium, DV_E_FORMATETC);
 
+#ifdef VBOX_DND_DEBUG_FORMATS
     LogFlowFunc(("pFormatEtc=%p, pMedium=%p\n", pFormatEtc, pMedium));
+#endif
 
     ULONG lIndex;
@@ -227,21 +231,5 @@
     if (mStatus == Dropped)
     {
-#if 0
-        LogFlowFunc(("Starting to transfer content from the guest ...\n"));
-
-        /*
-         * This is the right time starting to transfer the actual data
-         * from the guest:
-         * - The user just triggered an action that tells us to start, e.g. at
-         *   the moment we only "listen" for releasing the left mouse button.
-         * - We're still in OLE's (modal) drag'n drop context so that all data
-         *   from the guest (hopefully) will be available on the host when the
-         *   drag'n drop operation is about to finish through OLE.
-         *
-         * Note that in the URI case the guest files/dirs must be somewhere
-         * on the host before OLE's drag'n drop operation is finished, otherwise
-         * the whole operation will fail.
-         */
-#endif
+#ifdef VBOX_DND_DEBUG_FORMATS
         LogFlowFunc(("cfFormat=%RI16, sFormat=%s, tyMed=%RU32, dwAspect=%RU32\n",
                      pThisFormat->cfFormat, UIDnDDataObject::ClipboardFormatToString(pFormatEtc->cfFormat),
@@ -249,5 +237,5 @@
         LogFlowFunc(("Got strFormat=%s, pvData=%p, cbData=%RU32\n",
                      mstrFormat.toAscii().constData(), mpvData, mcbData));
-
+#endif
         QVariant::Type vaType;
         QString strMIMEType;
@@ -495,5 +483,4 @@
 STDMETHODIMP UIDnDDataObject::QueryGetData(FORMATETC *pFormatEtc)
 {
-    LogFlowFunc(("\n"));
     return (LookupFormatEtc(pFormatEtc, NULL /* puIndex */)) ? S_OK : DV_E_FORMATETC;
 }
@@ -648,7 +635,9 @@
             && pFormatEtc->dwAspect == mpFormatEtc[i].dwAspect)
         {
+#ifdef VBOX_DND_DEBUG_FORMATS
             LogFlowFunc(("Format found: tyMed=%RI32, cfFormat=%RI16, sFormats=%s, dwAspect=%RI32, ulIndex=%RU32\n",
                          pFormatEtc->tymed, pFormatEtc->cfFormat, UIDnDDataObject::ClipboardFormatToString(mpFormatEtc[i].cfFormat),
                          pFormatEtc->dwAspect, i));
+#endif
             if (puIndex)
                 *puIndex = i;
@@ -657,8 +646,9 @@
     }
 
+#ifdef VBOX_DND_DEBUG_FORMATS
     LogFlowFunc(("Format NOT found: tyMed=%RI32, cfFormat=%RI16, sFormats=%s, dwAspect=%RI32\n",
                  pFormatEtc->tymed, pFormatEtc->cfFormat, UIDnDDataObject::ClipboardFormatToString(pFormatEtc->cfFormat),
                  pFormatEtc->dwAspect));
-
+#endif
     return false;
 }
Index: /trunk/src/VBox/GuestHost/DragAndDrop/DnDURIList.cpp
===================================================================
--- /trunk/src/VBox/GuestHost/DragAndDrop/DnDURIList.cpp	(revision 50507)
+++ /trunk/src/VBox/GuestHost/DragAndDrop/DnDURIList.cpp	(revision 50508)
@@ -34,4 +34,180 @@
 #include <VBox/GuestHost/DragAndDrop.h>
 
+DnDURIObject::DnDURIObject(Type type,
+                           const RTCString &strSrcPath,
+                           const RTCString &strDstPath,
+                           uint32_t fMode, uint64_t cbSize)
+    : m_Type(type)
+    , m_strSrcPath(strSrcPath)
+    , m_strDstPath(strDstPath)
+    , m_fMode(fMode)
+    , m_cbSize(cbSize)
+    , m_cbProcessed(0)
+{
+    RT_ZERO(u);
+}
+
+DnDURIObject::~DnDURIObject(void)
+{
+    closeInternal();
+}
+
+void DnDURIObject::closeInternal(void)
+{
+    if (m_Type == File)
+    {
+        if (u.m_hFile)
+        {
+            RTFileClose(u.m_hFile);
+            u.m_hFile = NULL;
+        }
+    }
+}
+
+bool DnDURIObject::IsComplete(void) const
+{
+    bool fComplete = false;
+
+    Assert(m_cbProcessed <= m_cbSize);
+    if (m_cbProcessed == m_cbSize)
+        fComplete = true;
+
+    switch (m_Type)
+    {
+        case File:
+            if (!fComplete)
+                fComplete = !u.m_hFile;
+            break;
+
+        case Directory:
+            fComplete = true;
+            break;
+
+        default:
+            break;
+    }
+
+    return fComplete;
+}
+
+/* static */
+/** @todo Put this into an own class like DnDURIPath : public RTCString? */
+int DnDURIObject::RebaseURIPath(RTCString &strPath,
+                                const RTCString &strBaseOld,
+                                const RTCString &strBaseNew)
+{
+    int rc;
+    const char *pszPath = RTUriPath(strPath.c_str());
+    if (pszPath)
+    {
+        const char *pszPathStart = pszPath;
+        const char *pszBaseOld = strBaseOld.c_str();
+        if (   pszBaseOld
+            && RTPathStartsWith(pszPath, pszBaseOld))
+        {
+            pszPathStart += strlen(pszBaseOld);
+        }
+
+        rc = VINF_SUCCESS;
+
+        if (RT_SUCCESS(rc))
+        {
+            char *pszPathNew = RTPathJoinA(strBaseNew.c_str(), pszPathStart);
+            if (pszPathNew)
+            {
+                char *pszPathURI = RTUriCreate("file" /* pszScheme */, "/" /* pszAuthority */,
+                                               pszPathNew /* pszPath */,
+                                               NULL /* pszQuery */, NULL /* pszFragment */);
+                if (pszPathURI)
+                {
+#ifdef DEBUG_andy
+                    LogFlowFunc(("Rebasing \"%s\" to \"%s\"", strPath.c_str(), pszPathURI));
+#endif
+                    strPath = RTCString(pszPathURI) + "\r\n";
+                    RTStrFree(pszPathURI);
+
+                    rc = VINF_SUCCESS;
+                }
+                else
+                    rc = VERR_INVALID_PARAMETER;
+
+                RTStrFree(pszPathNew);
+            }
+            else
+                rc = VERR_NO_MEMORY;
+        }
+    }
+    else
+        rc = VERR_INVALID_PARAMETER;
+
+#ifdef DEBUG_andy
+    LogFlowFuncLeaveRC(rc);
+#endif
+    return rc;
+}
+
+int DnDURIObject::Read(void *pvBuf, uint32_t cbToRead, uint32_t *pcbRead)
+{
+    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+    AssertReturn(cbToRead, VERR_INVALID_PARAMETER);
+    /* pcbRead is optional. */
+
+    int rc;
+    switch (m_Type)
+    {
+        case File:
+        {
+            if (!u.m_hFile)
+            {
+                /* Open files on the source with RTFILE_O_DENY_WRITE to prevent races
+                 * where the OS writes to the file while the destination side transfers
+                 * it over. */
+                rc = RTFileOpen(&u.m_hFile, m_strSrcPath.c_str(),
+                                RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
+            }
+            else
+                rc = VINF_SUCCESS;
+
+            bool fDone = false;
+            if (RT_SUCCESS(rc))
+            {
+                size_t cbRead;
+                rc = RTFileRead(u.m_hFile, pvBuf, cbToRead, &cbRead);
+                if (RT_SUCCESS(rc))
+                {
+                    if (pcbRead)
+                        *pcbRead = (uint32_t)cbRead;
+
+                    m_cbProcessed += cbRead;
+                    Assert(m_cbProcessed <= m_cbSize);
+
+                    /* End of file reached or error occurred? */
+                    if (   cbRead < cbToRead
+                        || RT_FAILURE(rc))
+                        closeInternal();
+                }
+            }
+
+            break;
+        }
+
+        case Directory:
+        {
+            rc = VINF_SUCCESS;
+            break;
+        }
+
+        default:
+            rc = VERR_NOT_IMPLEMENTED;
+            break;
+    }
+
+    LogFlowFunc(("Returning strSourcePath=%s, rc=%Rrc\n",
+                 m_strSrcPath.c_str(), rc));
+    return rc;
+}
+
+/*** */
+
 DnDURIList::DnDURIList(void)
     : m_cbTotal(0)
@@ -47,5 +223,4 @@
 {
     AssertPtrReturn(pcszPath, VERR_INVALID_POINTER);
-    AssertReturn(cbBaseLen, VERR_INVALID_PARAMETER);
 
     RTFSOBJINFO objInfo;
@@ -74,9 +249,12 @@
         return rc;
 
-    m_lstTree.append(DnDURIPath(pcszPath, &pcszPath[cbBaseLen],
-                                objInfo.Attr.fMode, cbSize));
+    m_lstTree.append(DnDURIObject(  RTFS_IS_DIRECTORY(objInfo.Attr.fMode)
+                                  ? DnDURIObject::Directory
+                                  : DnDURIObject::File,
+                                  pcszPath, &pcszPath[cbBaseLen],
+                                  objInfo.Attr.fMode, cbSize));
     m_cbTotal += cbSize;
 #ifdef DEBUG_andy
-    LogFlowFunc(("strHostPath=%s, strGuestPath=%s, fMode=0x%x, cbSize=%RU64, cbTotal=%zu\n",
+    LogFlowFunc(("strSrcPath=%s, strDstPath=%s, fMode=0x%x, cbSize=%RU64, cbTotal=%zu\n",
                  pcszPath, &pcszPath[cbBaseLen], objInfo.Attr.fMode, cbSize, m_cbTotal));
 #endif
@@ -140,9 +318,10 @@
                         break;
 
-                    m_lstTree.append(DnDURIPath(pszNewFile, &pszNewFile[cbBaseLen],
-                                                objInfo1.Attr.fMode, cbSize));
+                    m_lstTree.append(DnDURIObject(DnDURIObject::File,
+                                                  pszNewFile, &pszNewFile[cbBaseLen],
+                                                  objInfo1.Attr.fMode, cbSize));
                     m_cbTotal += cbSize;
 #ifdef DEBUG_andy
-                    LogFlowFunc(("strHostPath=%s, strGuestPath=%s, fMode=0x%x, cbSize=%RU64, cbTotal=%zu\n",
+                    LogFlowFunc(("strSrcPath=%s, strDstPath=%s, fMode=0x%x, cbSize=%RU64, cbTotal=%zu\n",
                                  pszNewFile, &pszNewFile[cbBaseLen], objInfo1.Attr.fMode, cbSize, m_cbTotal));
 #endif
@@ -163,10 +342,56 @@
 }
 
-int DnDURIList::AppendPath(const char *pszPath, uint32_t fFlags)
+int DnDURIList::AppendNativePath(const char *pszPath, uint32_t fFlags)
 {
     AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
 
+    char *pszPathURI = RTUriCreate("file" /* pszScheme */, "/" /* pszAuthority */,
+                                   pszPath, NULL /* pszQuery */, NULL /* pszFragment */);
+    int rc;
+    if (pszPathURI)
+    {
+        rc = AppendURIPath(pszPathURI, fFlags);
+        RTStrFree(pszPathURI);
+    }
+    else
+        rc = VERR_INVALID_PARAMETER;
+
+    return rc;
+}
+
+int DnDURIList::AppendNativePathsFromList(const char *pszNativePaths, size_t cbNativePaths,
+                                          uint32_t fFlags)
+{
+    AssertPtrReturn(pszNativePaths, VERR_INVALID_POINTER);
+    AssertReturn(cbNativePaths, VERR_INVALID_PARAMETER);
+
+    RTCList<RTCString> lstPaths
+        = RTCString(pszNativePaths, cbNativePaths - 1).split("\r\n");
+    return AppendNativePathsFromList(lstPaths, fFlags);
+}
+
+int DnDURIList::AppendNativePathsFromList(const RTCList<RTCString> &lstNativePaths,
+                                          uint32_t fFlags)
+{
+    int rc = VINF_SUCCESS;
+
+    for (size_t i = 0; i < lstNativePaths.size(); i++)
+    {
+        const RTCString &strPath = lstNativePaths.at(i);
+        rc = AppendNativePath(strPath.c_str(), fFlags);
+        if (RT_FAILURE(rc))
+            break;
+    }
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+int DnDURIList::AppendURIPath(const char *pszURI, uint32_t fFlags)
+{
+    AssertPtrReturn(pszURI, VERR_INVALID_POINTER);
+
 #ifdef DEBUG_andy
-    LogFlowFunc(("pszPath=%s, fFlags=0x%x\n", pszPath, fFlags));
+    LogFlowFunc(("pszPath=%s, fFlags=0x%x\n", pszURI, fFlags));
 #endif
     int rc = VINF_SUCCESS;
@@ -174,5 +399,5 @@
     /* Query the path component of a file URI. If this hasn't a
      * file scheme NULL is returned. */
-    char *pszFilePath = RTUriFilePath(pszPath, URI_FILE_FORMAT_AUTO);
+    char *pszFilePath = RTUriFilePath(pszURI, URI_FILE_FORMAT_AUTO);
     if (pszFilePath)
     {
@@ -182,20 +407,22 @@
         if (pszFileName)
         {
-            char *pszNewURI = RTUriFileCreate(pszFileName);
-            if (pszNewURI)
-            {
-                m_lstRoot.append(pszNewURI);
-                RTStrFree(pszNewURI);
-
-                rc = appendPathRecursive(pszFilePath,
-                                         pszFileName - pszFilePath,
-                                         fFlags);
-            }
-        }
+            Assert(pszFileName >= pszFilePath);
+            char *pszRoot = &pszFilePath[pszFileName - pszFilePath];
+            m_lstRoot.append(pszRoot);
+#ifdef DEBUG_andy
+            LogFlowFunc(("pszFilePath=%s, pszFileName=%s, pszRoot=%s\n",
+                         pszFilePath, pszFileName, pszRoot));
+#endif
+            rc = appendPathRecursive(pszFilePath,
+                                     pszFileName - pszFilePath,
+                                     fFlags);
+        }
+        else
+            rc = VERR_NOT_FOUND;
 
         RTStrFree(pszFilePath);
     }
-    else /* Just append the raw data. */
-        m_lstRoot.append(pszPath);
+    else
+        rc = VERR_INVALID_PARAMETER;
 
     LogFlowFuncLeaveRC(rc);
@@ -203,6 +430,17 @@
 }
 
-int DnDURIList::AppendPathsFromList(const RTCList<RTCString> &lstURI,
-                                    uint32_t fFlags)
+int DnDURIList::AppendURIPathsFromList(const char *pszURIPaths, size_t cbURIPaths,
+                                       uint32_t fFlags)
+{
+    AssertPtrReturn(pszURIPaths, VERR_INVALID_POINTER);
+    AssertReturn(cbURIPaths, VERR_INVALID_PARAMETER);
+
+    RTCList<RTCString> lstPaths
+        = RTCString(pszURIPaths, cbURIPaths - 1).split("\r\n");
+    return AppendURIPathsFromList(lstPaths, fFlags);
+}
+
+int DnDURIList::AppendURIPathsFromList(const RTCList<RTCString> &lstURI,
+                                       uint32_t fFlags)
 {
     int rc = VINF_SUCCESS;
@@ -210,6 +448,7 @@
     for (size_t i = 0; i < lstURI.size(); i++)
     {
-        const RTCString &strURI = lstURI.at(i);
-        rc = AppendPath(strURI.c_str(), fFlags);
+        RTCString strURI = lstURI.at(i);
+        rc = AppendURIPath(strURI.c_str(), fFlags);
+
         if (RT_FAILURE(rc))
             break;
@@ -228,12 +467,145 @@
 }
 
+#if 0
+int DnDURIList::FromData(const void *pvData, size_t cbData,
+
+                         uint32_t fFlags)
+{
+    AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+    AssertReturn(cbData, VERR_INVALID_PARAMETER);
+
+    RTCList<RTCString> lstURI =
+        RTCString(static_cast<const char*>(pvData), cbData - 1).split("\r\n");
+    if (lstURI.isEmpty())
+        return VINF_SUCCESS;
+
+    int rc = VINF_SUCCESS;
+
+    for (size_t i = 0; i < lstURI.size(); ++i)
+    {
+        const RTCString &strUri = lstURI.at(i);
+        /* Query the path component of a file URI. If this hasn't a
+         * file scheme, null is returned. */
+        char *pszFilePath = RTUriFilePath(strUri.c_str(), URI_FILE_FORMAT_AUTO);
+        if (pszFilePath)
+        {
+            rc = DnDPathSanitize(pszFilePath, strlen(pszFilePath));
+            if (RT_SUCCESS(rc))
+            {
+                /** @todo Use RTPathJoin? */
+                RTCString strFullPath;
+                if (strBasePath.isNotEmpty())
+                    strFullPath = RTCString().printf("%s%c%s", strBasePath.c_str(),
+                                                     RTPATH_SLASH, pszFilePath);
+                else
+                    strFullPath = pszFilePath;
+
+                char *pszNewUri = RTUriFileCreate(strFullPath.c_str());
+                if (pszNewUri)
+                {
+                    m_lstRoot.append(pszNewUri);
+                    RTStrFree(pszNewUri);
+                }
+            }
+        }
+        else
+            rc = VERR_INVALID_PARAMETER;
+
+        if (RT_FAILURE(rc))
+            break;
+    }
+
+    return rc;
+}
+#endif
+
 void DnDURIList::RemoveFirst(void)
 {
-    const DnDURIPath &curPath = m_lstTree.first();
-
-    Assert(m_cbTotal >= curPath.m_cbSize);
-    m_cbTotal -= curPath.m_cbSize; /* Adjust total size. */
+    DnDURIObject &curPath = m_lstTree.first();
+
+    uint64_t cbSize = curPath.GetSize();
+    Assert(m_cbTotal >= cbSize);
+    m_cbTotal -= cbSize; /* Adjust total size. */
 
     m_lstTree.removeFirst();
 }
 
+int DnDURIList::RootFromURIData(const void *pvData, size_t cbData,
+                                uint32_t fFlags)
+{
+    AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+    AssertReturn(cbData, VERR_INVALID_PARAMETER);
+
+    RTCList<RTCString> lstURI =
+        RTCString(static_cast<const char*>(pvData), cbData - 1).split("\r\n");
+    if (lstURI.isEmpty())
+        return VINF_SUCCESS;
+
+    int rc = VINF_SUCCESS;
+
+    for (size_t i = 0; i < lstURI.size(); ++i)
+    {
+        /* Query the path component of a file URI. If this hasn't a
+         * file scheme, NULL is returned. */
+        const char *pszURI = lstURI.at(i).c_str();
+        char *pszFilePath = RTUriFilePath(pszURI,
+                                          URI_FILE_FORMAT_AUTO);
+#ifdef DEBUG_andy
+        LogFlowFunc(("pszURI=%s, pszFilePath=%s\n", pszURI, pszFilePath));
+#endif
+        if (pszFilePath)
+        {
+            rc = DnDPathSanitize(pszFilePath, strlen(pszFilePath));
+            if (RT_SUCCESS(rc))
+                m_lstRoot.append(pszFilePath);
+
+            RTStrFree(pszFilePath);
+        }
+        else
+            rc = VERR_INVALID_PARAMETER;
+
+        if (RT_FAILURE(rc))
+            break;
+    }
+
+    return rc;
+}
+
+RTCString DnDURIList::RootToString(const RTCString &strBasePath /* = "" */)
+{
+    RTCString strRet;
+    for (size_t i = 0; i < m_lstRoot.size(); i++)
+    {
+        const char *pszCurRoot = m_lstRoot.at(i).c_str();
+        if (strBasePath.isNotEmpty())
+        {
+            char *pszPath = RTPathJoinA(strBasePath.c_str(), pszCurRoot);
+            if (pszPath)
+            {
+                char *pszPathURI = RTUriFileCreate(pszPath);
+                if (pszPathURI)
+                {
+                    strRet += RTCString(pszPathURI) + "\r\n";
+                    RTStrFree(pszPathURI);
+                }
+                RTStrFree(pszPath);
+            }
+            else
+                break;
+        }
+        else
+        {
+            char *pszPathURI = RTUriFileCreate(pszCurRoot);
+            if (pszPathURI)
+            {
+                strRet += RTCString(pszPathURI) + "\r\n";
+                RTStrFree(pszPathURI);
+            }
+            else
+                break;
+        }
+    }
+
+    return strRet;
+}
+
Index: /trunk/src/VBox/HostServices/DragAndDrop/dndmanager.cpp
===================================================================
--- /trunk/src/VBox/HostServices/DragAndDrop/dndmanager.cpp	(revision 50507)
+++ /trunk/src/VBox/HostServices/DragAndDrop/dndmanager.cpp	(revision 50508)
@@ -49,16 +49,14 @@
 public:
 
-    DnDHGSendDirPrivate(const RTCString &strPath,
-                        uint32_t fMode, uint64_t cbSize,
+    DnDHGSendDirPrivate(DnDURIObject URIObject,
                         PFNDNDPRIVATEPROGRESS pfnProgressCallback, void *pvProgressUser)
-        : m_strPath(strPath)
-        , m_cbSize(cbSize)
+        : m_URIObject(URIObject)
         , m_pfnProgressCallback(pfnProgressCallback)
         , m_pvProgressUser(pvProgressUser)
     {
         VBOXHGCMSVCPARM paTmpParms[3];
-        paTmpParms[0].setString(m_strPath.c_str());
-        paTmpParms[1].setUInt32((uint32_t)(m_strPath.length() + 1));
-        paTmpParms[2].setUInt32(fMode);
+        paTmpParms[0].setString(m_URIObject.GetDestPath().c_str());
+        paTmpParms[1].setUInt32((uint32_t)(m_URIObject.GetDestPath().length() + 1));
+        paTmpParms[2].setUInt32(m_URIObject.GetMode());
 
         m_pNextMsg = new HGCM::Message(DragAndDropSvc::HOST_DND_HG_SND_DIR, 3, paTmpParms);
@@ -71,5 +69,5 @@
         if (   RT_SUCCESS(rc)
             && m_pfnProgressCallback)
-            rc = m_pfnProgressCallback(m_cbSize, m_pvProgressUser);
+            rc = m_pfnProgressCallback(m_URIObject.GetSize(), m_pvProgressUser);
 
         return rc;
@@ -77,8 +75,8 @@
 
 protected:
-    RTCString              m_strPath;
+
+    DnDURIObject           m_URIObject;
 
     /* Progress stuff */
-    size_t                 m_cbSize;
     PFNDNDPRIVATEPROGRESS  m_pfnProgressCallback;
     void                  *m_pvProgressUser;
@@ -94,7 +92,5 @@
 public:
 
-    DnDHGSendFilePrivate(const RTCString &strHostPath,
-                         const RTCString &strGuestPath,
-                         uint32_t fMode, uint64_t cbSize,
+    DnDHGSendFilePrivate(DnDURIObject URIObject,
                          PFNDNDPRIVATEPROGRESS pfnProgressCallback, void *pvProgressUser);
     virtual ~DnDHGSendFilePrivate(void);
@@ -103,9 +99,6 @@
 
 protected:
-    RTCString              m_strHostPath;
-    RTCString              m_strGuestPath;
-    uint64_t               m_cbFileSize;
-    uint64_t               m_cbFileProcessed;
-    RTFILE                 m_hCurFile;
+
+    DnDURIObject           m_URIObject;
     VBOXHGCMSVCPARM        m_paSkelParms[5];
 
@@ -144,23 +137,18 @@
 
 /******************************************************************************
- *   DnDHGSendFilePrivate                                                *
+ *   DnDHGSendFilePrivate                                                     *
  ******************************************************************************/
 
-DnDHGSendFilePrivate::DnDHGSendFilePrivate(const RTCString &strHostPath, const RTCString &strGuestPath,
-                                           uint32_t fMode, uint64_t cbSize,
+DnDHGSendFilePrivate::DnDHGSendFilePrivate(DnDURIObject URIObject,
                                            PFNDNDPRIVATEPROGRESS pfnProgressCallback, void *pvProgressUser)
-    : m_strHostPath(strHostPath)
-    , m_strGuestPath(strGuestPath)
-    , m_cbFileSize(cbSize)
-    , m_cbFileProcessed(0)
-    , m_hCurFile(0)
+    : m_URIObject(URIObject)
     , m_pfnProgressCallback(pfnProgressCallback)
     , m_pvProgressUser(pvProgressUser)
 {
-    m_paSkelParms[0].setString(m_strGuestPath.c_str());
-    m_paSkelParms[1].setUInt32((uint32_t)(m_strGuestPath.length() + 1));
+    m_paSkelParms[0].setString(m_URIObject.GetDestPath().c_str());
+    m_paSkelParms[1].setUInt32((uint32_t)(m_URIObject.GetDestPath().length() + 1));
     m_paSkelParms[2].setPointer(NULL, 0);
     m_paSkelParms[3].setUInt32(0);
-    m_paSkelParms[4].setUInt32(fMode);
+    m_paSkelParms[4].setUInt32(m_URIObject.GetMode());
 
     m_pNextMsg = new HGCM::Message(DragAndDropSvc::HOST_DND_HG_SND_FILE, 5, m_paSkelParms);
@@ -169,6 +157,4 @@
 DnDHGSendFilePrivate::~DnDHGSendFilePrivate(void)
 {
-    if (m_hCurFile)
-        RTFileClose(m_hCurFile);
 }
 
@@ -184,13 +170,5 @@
         return rc;
 
-    if (!m_hCurFile)
-    {
-        /* Open files on the host with RTFILE_O_DENY_WRITE to prevent races where the host
-         * writes to the file while the guest transfers it over. */
-        rc = RTFileOpen(&m_hCurFile, m_strHostPath.c_str(),
-                        RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
-    }
-
-    size_t cbRead;
+    uint32_t cbRead;
     if (RT_SUCCESS(rc))
     {
@@ -200,11 +178,8 @@
         void *pvBuf = paParms[2].u.pointer.addr;
         AssertPtr(pvBuf);
-        rc = RTFileRead(m_hCurFile, pvBuf, cbToRead, &cbRead);
+
+        rc = m_URIObject.Read(pvBuf, cbToRead, &cbRead);
         if (RT_LIKELY(RT_SUCCESS(rc)))
         {
-            /* Advance. */
-            m_cbFileProcessed += cbRead;
-            Assert(m_cbFileProcessed <= m_cbFileSize);
-
             /* Tell the guest the actual size. */
             paParms[3].setUInt32((uint32_t)cbRead);
@@ -214,12 +189,9 @@
     if (RT_SUCCESS(rc))
     {
-        /* Check if we are done. */
-        Assert(m_cbFileProcessed <= m_cbFileSize);
-        bool fDone = m_cbFileSize == m_cbFileProcessed;
-        if (!fDone)
+        if (!m_URIObject.IsComplete())
         {
             try
             {
-                /* More data! Prepare the next message. */
+                /* More data needed to send over. Prepare the next message. */
                 m_pNextMsg = new HGCM::Message(DragAndDropSvc::HOST_DND_HG_SND_FILE, 5 /* cParms */,
                                                m_paSkelParms);
@@ -236,11 +208,4 @@
         {
             rc = m_pfnProgressCallback(cbRead, m_pvProgressUser);
-        }
-
-        if (   fDone
-            || RT_FAILURE(rc))
-        {
-            RTFileClose(m_hCurFile);
-            m_hCurFile = NIL_RTFILE;
         }
     }
@@ -365,14 +330,14 @@
         if (!lstURIOrg.isEmpty())
         {
-            rc = m_lstURI.AppendPathsFromList(lstURIOrg, 0 /* fFlags */);
+            rc = m_lstURI.AppendNativePathsFromList(lstURIOrg, 0 /* fFlags */);
             if (RT_SUCCESS(rc))
             {
                 /* Add the total size of all meta data + files transferred to
-                 * the message's total count. */
+                 * the message's total byte count. */
                 m_cbTotal += m_lstURI.TotalBytes();
 
                 /* We have to change the actual DnD data. Remove any host paths and
-                 * just decode the filename into the new data. The guest tools will
-                 * add the correct path again, before sending the DnD drop event to
+                 * just decode the filename into the new data. The Guest Additions will
+                 * add the correct path again before sending the DnD drop event to
                  * some window. */
                 strNewURIs = m_lstURI.RootToString();
@@ -445,25 +410,24 @@
         /* Create new messages based on our internal path list. Currently
          * this could be directories or regular files. */
-        const DnDURIPath &nextPath = m_lstURI.First();
+        const DnDURIObject &nextObj = m_lstURI.First();
         try
         {
+            uint32_t fMode = nextObj.GetMode();
             LogFlowFunc(("Processing srcPath=%s, dstPath=%s, fMode=0x%x, cbSize=%RU32, fIsDir=%RTbool, fIsFile=%RTbool\n",
-                         nextPath.m_strSrcPath.c_str(), nextPath.m_strDstPath.c_str(),
-                         nextPath.m_fMode, nextPath.m_cbSize,
-                         RTFS_IS_DIRECTORY(nextPath.m_fMode), RTFS_IS_FILE(nextPath.m_fMode)));
-
-            if (RTFS_IS_DIRECTORY(nextPath.m_fMode))
-                m_pNextPathMsg = new DnDHGSendDirPrivate(nextPath.m_strDstPath,
-                                                         nextPath.m_fMode, nextPath.m_cbSize,
-                                                         &DnDHGSendDataMessage::progressCallback,
+                         nextObj.GetSourcePath().c_str(), nextObj.GetDestPath().c_str(),
+                         fMode, nextObj.GetSize(),
+                         RTFS_IS_DIRECTORY(fMode), RTFS_IS_FILE(fMode)));
+
+            if (RTFS_IS_DIRECTORY(fMode))
+                m_pNextPathMsg = new DnDHGSendDirPrivate(nextObj,
+                                                         &DnDHGSendDataMessage::progressCallback /* pfnProgressCallback */,
                                                          this /* pvProgressUser */);
-            else if (RTFS_IS_FILE(nextPath.m_fMode))
-                m_pNextPathMsg = new DnDHGSendFilePrivate(nextPath.m_strSrcPath, nextPath.m_strDstPath,
-                                                          nextPath.m_fMode, nextPath.m_cbSize,
-                                                          &DnDHGSendDataMessage::progressCallback,
+            else if (RTFS_IS_FILE(fMode))
+                m_pNextPathMsg = new DnDHGSendFilePrivate(nextObj,
+                                                          &DnDHGSendDataMessage::progressCallback /* pfnProgressCallback */,
                                                           this /* pvProgressUser */);
             else
                 AssertMsgFailedReturn(("fMode=0x%x is not supported for srcPath=%s, dstPath=%s\n",
-                                       nextPath.m_fMode, nextPath.m_strSrcPath.c_str(), nextPath.m_strDstPath.c_str()),
+                                       fMode, nextObj.GetSourcePath().c_str(), nextObj.GetDestPath().c_str()),
                                        VERR_NO_DATA);
 
Index: /trunk/src/VBox/HostServices/DragAndDrop/service.cpp
===================================================================
--- /trunk/src/VBox/HostServices/DragAndDrop/service.cpp	(revision 50507)
+++ /trunk/src/VBox/HostServices/DragAndDrop/service.cpp	(revision 50508)
@@ -169,177 +169,248 @@
 }
 
-void DragAndDropService::guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+void DragAndDropService::guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
+                                   void *pvClient, uint32_t u32Function,
+                                   uint32_t cParms, VBOXHGCMSVCPARM paParms[])
 {
     LogFlowFunc(("u32ClientID=%RU32, u32Function=%RU32, cParms=%RU32\n",
                  u32ClientID, u32Function, cParms));
 
-    int rc = VINF_SUCCESS;
+    /* Check if we've the right mode set. */
+    bool fIgnoreRequest = true; /* Play safe. */
     switch (u32Function)
     {
-        /* Note: Older VBox versions with enabled DnD guest->host support (< 4.4)
-         *       used the same 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 DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG:
-        {
-            LogFlowFunc(("GUEST_DND_GET_NEXT_HOST_MSG\n"));
-            if (   cParms != 3
-                || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* message */
-                || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* parameter count */
-                || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* blocking */)
-                rc = VERR_INVALID_PARAMETER;
+            if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
+            {
+                fIgnoreRequest = false;
+            }
             else
-            {
-                rc = m_pManager->nextMessageInfo(&paParms[0].u.uint32, &paParms[1].u.uint32);
-                if (   RT_FAILURE(rc)
-                    && paParms[2].u.uint32) /* Blocking? */
-                {
-                    m_clientQueue.append(new HGCM::Client(u32ClientID, callHandle, u32Function, cParms, paParms));
-                    rc = VINF_HGCM_ASYNC_EXECUTE;
-                }
-            }
+                LogFlowFunc(("Drag'n drop disabled, ignoring request\n"));
             break;
-        }
         case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
-        {
-            LogFlowFunc(("GUEST_DND_HG_ACK_OP\n"));
-            if (   modeGet() != VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
-                && modeGet() != VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST)
-            {
-                LogFlowFunc(("Wrong DnD mode, ignoring request\n"));
-                break;
-            }
-
-            if (   cParms != 1
-                || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* action */)
-                rc = VERR_INVALID_PARAMETER;
+        case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
+            if (   modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
+                || modeGet() == VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST)
+            {
+                fIgnoreRequest = false;
+            }
             else
-            {
-                DragAndDropSvc::VBOXDNDCBHGACKOPDATA data;
-                data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP;
-                paParms[0].getUInt32(&data.uAction);
-                if (m_pfnHostCallback)
-                    rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
-//                m_pHelpers->pfnCallComplete(callHandle, rc);
-            }
+                LogFlowFunc(("Host -> guest DnD mode disabled, ignoring request\n"));
             break;
-        }
-        case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
-        {
-            LogFlowFunc(("GUEST_DND_HG_REQ_DATA\n"));
-            if (   modeGet() != VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
-                && modeGet() != VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST)
-            {
-                LogFlowFunc(("Wrong DnD mode, ignoring request\n"));
-                break;
-            }
-
-            if (   cParms != 1
-                || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* format */)
-                rc = VERR_INVALID_PARAMETER;
-            else
-            {
-                DragAndDropSvc::VBOXDNDCBHGREQDATADATA data;
-                data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA;
-                uint32_t cTmp;
-                paParms[0].getPointer((void**)&data.pszFormat, &cTmp);
-                if (m_pfnHostCallback)
-                    rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
-//                m_pHelpers->pfnCallComplete(callHandle, rc);
-//                if (data.pszFormat)
-//                    RTMemFree(data.pszFormat);
-//                if (data.pszTmpPath)
-//                    RTMemFree(data.pszTmpPath);
-            }
-            break;
-        }
 #ifdef VBOX_WITH_DRAG_AND_DROP_GH
         case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
-        {
-            LogFlowFunc(("GUEST_DND_GH_ACK_PENDING\n"));
-            if (   modeGet() != VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
-                && modeGet() != VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST)
-            {
-                LogFlowFunc(("Wrong DnD mode, ignoring request\n"));
-                break;
-            }
-
-            if (   cParms != 3
-                || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* defaction */
-                || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* allactions */
-                || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR   /* format */)
-                rc = VERR_INVALID_PARAMETER;
+        case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
+        case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
+        case DragAndDropSvc::GUEST_DND_GH_SND_FILE:
+        case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
+            if (   modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
+                || modeGet() == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST)
+            {
+                fIgnoreRequest = false;
+            }
             else
-            {
-                DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA data;
-                data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING;
-                paParms[0].getUInt32(&data.uDefAction);
-                paParms[1].getUInt32(&data.uAllActions);
-                uint32_t cTmp;
-                paParms[2].getPointer((void**)&data.pszFormat, &cTmp);
-                if (m_pfnHostCallback)
-                    rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
-            }
+                LogFlowFunc(("Guest -> host DnD mode disabled, ignoring request\n"));
             break;
-        }
-        case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
-        {
-            LogFlowFunc(("GUEST_DND_GH_SND_DATA\n"));
-            if (   modeGet() != VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
-                && modeGet() != VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST)
-            {
-                LogFlowFunc(("Wrong DnD mode, ignoring request\n"));
-                break;
-            }
-
-            if (   cParms != 2
-                || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR   /* data */
-                || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* size */)
-                rc = VERR_INVALID_PARAMETER;
-            else
-            {
-                DragAndDropSvc::VBOXDNDCBSNDDATADATA data;
-                data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA;
-                paParms[0].getPointer((void**)&data.pvData, &data.cbData);
-                paParms[1].getUInt32(&data.cbAllSize);
-                if (m_pfnHostCallback)
-                    rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
-            }
-            break;
-        }
-        case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
-        {
-            LogFlowFunc(("GUEST_DND_GH_EVT_ERROR\n"));
-            if (   modeGet() != VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
-                && modeGet() != VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST)
-            {
-                LogFlowFunc(("Wrong DnD mode, ignoring request\n"));
-                break;
-            }
-
-            if (   cParms != 1
-                || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* rc */)
-                rc = VERR_INVALID_PARAMETER;
-            else
-            {
-                DragAndDropSvc::VBOXDNDCBEVTERRORDATA data;
-                data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR;
-                uint32_t rcOp;
-                paParms[0].getUInt32(&rcOp);
-                data.rc = rcOp;
-                if (m_pfnHostCallback)
-                    rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
-            }
-            break;
-        }
 #endif
         default:
+            /* Reach through to DnD manager. */
+            fIgnoreRequest = false;
+            break;
+    }
+
+    int rc;
+    if (!fIgnoreRequest)
+    {
+        rc = VINF_SUCCESS;
+        switch (u32Function)
         {
-            /* All other messages are handled by the DnD manager. */
-            rc = m_pManager->nextMessage(u32Function, cParms, paParms);
-            break;
+            /* Note: Older VBox versions with enabled DnD guest->host support (< 4.4)
+             *       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 DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG:
+            {
+                LogFlowFunc(("GUEST_DND_GET_NEXT_HOST_MSG\n"));
+                if (   cParms != 3
+                    || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* message */
+                    || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* parameter count */
+                    || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* blocking */)
+                    rc = VERR_INVALID_PARAMETER;
+                else
+                {
+                    rc = m_pManager->nextMessageInfo(&paParms[0].u.uint32, &paParms[1].u.uint32);
+                    if (   RT_FAILURE(rc)
+                        && paParms[2].u.uint32) /* Blocking? */
+                    {
+                        m_clientQueue.append(new HGCM::Client(u32ClientID, callHandle, u32Function, cParms, paParms));
+                        rc = VINF_HGCM_ASYNC_EXECUTE;
+                    }
+                }
+                break;
+            }
+            case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
+            {
+                LogFlowFunc(("GUEST_DND_HG_ACK_OP\n"));
+                if (   cParms != 1
+                    || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* action */)
+                    rc = VERR_INVALID_PARAMETER;
+                else
+                {
+                    DragAndDropSvc::VBOXDNDCBHGACKOPDATA data;
+                    data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP;
+                    paParms[0].getUInt32(&data.uAction);
+                    if (m_pfnHostCallback)
+                        rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
+    //                m_pHelpers->pfnCallComplete(callHandle, rc);
+                }
+                break;
+            }
+            case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
+            {
+                LogFlowFunc(("GUEST_DND_HG_REQ_DATA\n"));
+                if (   cParms != 1
+                    || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* format */)
+                    rc = VERR_INVALID_PARAMETER;
+                else
+                {
+                    DragAndDropSvc::VBOXDNDCBHGREQDATADATA data;
+                    data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA;
+                    uint32_t cTmp;
+                    paParms[0].getPointer((void**)&data.pszFormat, &cTmp);
+                    if (m_pfnHostCallback)
+                        rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
+    //                m_pHelpers->pfnCallComplete(callHandle, rc);
+    //                if (data.pszFormat)
+    //                    RTMemFree(data.pszFormat);
+    //                if (data.pszTmpPath)
+    //                    RTMemFree(data.pszTmpPath);
+                }
+                break;
+            }
+#ifdef VBOX_WITH_DRAG_AND_DROP_GH
+            case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
+            {
+                LogFlowFunc(("GUEST_DND_GH_ACK_PENDING\n"));
+                if (   cParms != 3
+                    || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* defaction */
+                    || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* allactions */
+                    || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR   /* format */)
+                    rc = VERR_INVALID_PARAMETER;
+                else
+                {
+                    DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA data;
+                    data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING;
+                    paParms[0].getUInt32(&data.uDefAction);
+                    paParms[1].getUInt32(&data.uAllActions);
+                    uint32_t cTmp;
+                    paParms[2].getPointer((void**)&data.pszFormat, &cTmp);
+                    if (m_pfnHostCallback)
+                        rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
+                }
+                break;
+            }
+            case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
+            {
+                LogFlowFunc(("GUEST_DND_GH_SND_DATA\n"));
+                if (   cParms != 2
+                    || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR   /* data */
+                    || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* size */)
+                    rc = VERR_INVALID_PARAMETER;
+                else
+                {
+                    DragAndDropSvc::VBOXDNDCBSNDDATADATA data;
+                    data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA;
+                    paParms[0].getPointer((void**)&data.pvData, &data.cbData);
+                    paParms[1].getUInt32(&data.cbTotalSize);
+                    if (m_pfnHostCallback)
+                        rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
+                }
+                break;
+            }
+            case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
+            {
+                LogFlowFunc(("GUEST_DND_GH_SND_DIR\n"));
+                if (   cParms != 3
+                    || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR   /* path */
+                    || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* path length */
+                    || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* creation mode */)
+                    rc = VERR_INVALID_PARAMETER;
+                else
+                {
+                    DragAndDropSvc::VBOXDNDCBSNDDIRDATA data;
+                    data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR;
+                    uint32_t cTmp;
+                    paParms[0].getPointer((void**)&data.pszPath, &cTmp);
+                    paParms[1].getUInt32(&data.cbPath);
+                    paParms[2].getUInt32(&data.fMode);
+#ifdef DEBUG_andy
+                    LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x\n",
+                                 data.pszPath, data.cbPath, data.fMode));
+#endif
+                    if (m_pfnHostCallback)
+                        rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
+                }
+                break;
+            }
+            case DragAndDropSvc::GUEST_DND_GH_SND_FILE:
+            {
+                LogFlowFunc(("GUEST_DND_GH_SND_FILE\n"));
+                if (   cParms != 5
+                    || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR   /* file path */
+                    || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* file path length */
+                    || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR   /* file data */
+                    || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* file data length */
+                    || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* creation mode */)
+                    rc = VERR_INVALID_PARAMETER;
+                else
+                {
+                    DragAndDropSvc::VBOXDNDCBSNDFILEDATA data;
+                    data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE;
+                    uint32_t cTmp;
+                    paParms[0].getPointer((void**)&data.pszFilePath, &cTmp);
+                    paParms[1].getUInt32(&data.cbFilePath);
+                    paParms[2].getPointer((void**)&data.pvData, &data.cbData);
+                    /* paParms[3] is cbData. */
+                    paParms[4].getUInt32(&data.fMode);
+#ifdef DEBUG_andy
+                    LogFlowFunc(("pszFilePath=%s, cbData=%RU32, pvData=0x%p, fMode=0x%x\n",
+                                 data.pszFilePath, data.cbData, data.pvData, data.fMode));
+#endif
+                    if (m_pfnHostCallback)
+                        rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
+                }
+                break;
+            }
+            case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
+            {
+                LogFlowFunc(("GUEST_DND_GH_EVT_ERROR\n"));
+                if (   cParms != 1
+                    || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* rc */)
+                    rc = VERR_INVALID_PARAMETER;
+                else
+                {
+                    DragAndDropSvc::VBOXDNDCBEVTERRORDATA data;
+                    data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR;
+                    uint32_t rcOp;
+                    paParms[0].getUInt32(&rcOp);
+                    data.rc = rcOp;
+                    if (m_pfnHostCallback)
+                        rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
+                }
+                break;
+            }
+#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
+            default:
+            {
+                /* All other messages are handled by the DnD manager. */
+                rc = m_pManager->nextMessage(u32Function, cParms, paParms);
+                break;
+            }
         }
     }
+    else
+        rc = VERR_NOT_SUPPORTED;
+
     /* If async execute is requested, we didn't notify the guest about
      * completion. The client is queued into the waiters list and will be
Index: /trunk/src/VBox/Main/include/GuestDnDImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/GuestDnDImpl.h	(revision 50507)
+++ /trunk/src/VBox/Main/include/GuestDnDImpl.h	(revision 50508)
@@ -4,5 +4,5 @@
 
 /*
- * Copyright (C) 2011-2012 Oracle Corporation
+ * Copyright (C) 2011-2014 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -20,4 +20,7 @@
 /* Forward declaration of the d-pointer. */
 class GuestDnDPrivate;
+#ifdef VBOX_WITH_DRAG_AND_DROP_GH
+ class DnDGuestResponse;
+#endif
 
 class GuestDnD
@@ -42,4 +45,12 @@
     static DECLCALLBACK(int) notifyGuestDragAndDropEvent(void *pvExtension, uint32_t u32Function, void *pvParms, uint32_t cbParms);
 
+protected:
+
+#ifdef VBOX_WITH_DRAG_AND_DROP_GH
+    int onGHSendData(DnDGuestResponse *pResp, const void *pvData, size_t cbData, size_t cbTotalSize);
+    int onGHSendDir(DnDGuestResponse *pResp, const char *pszPath, size_t cbPath, uint32_t fMode);
+    int onGHSendFile(DnDGuestResponse *pResp, const char *pszPath, size_t cbPath, void *pvData, size_t cbData, uint32_t fMode);
+#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
+
 private:
 
Index: /trunk/src/VBox/Main/src-client/GuestDnDImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/GuestDnDImpl.cpp	(revision 50507)
+++ /trunk/src/VBox/Main/src-client/GuestDnDImpl.cpp	(revision 50508)
@@ -191,5 +191,9 @@
     Utf8Str format(void) const { return m_strFormat; }
 
-    int dataAdd(void *pvData, uint32_t cbData, uint32_t *pcbCurSize);
+    void setDropDir(const Utf8Str &strDropDir) { m_strDropDir = strDropDir; }
+    Utf8Str dropDir(void) const { return m_strDropDir; }
+
+    int dataAdd(const void *pvData, uint32_t cbData, uint32_t *pcbCurSize);
+    int dataSetStatus(size_t cbDataAdd, size_t cbDataTotal = 0);
     void reset(void);
     const void *data(void) { return m_pvData; }
@@ -205,6 +209,14 @@
     uint32_t             m_allActions;
     Utf8Str              m_strFormat;
+
+    /** The actual MIME data.*/
     void                *m_pvData;
+    /** Size (in bytes) of MIME data. */
     uint32_t             m_cbData;
+
+    size_t               m_cbDataCurrent;
+    size_t               m_cbDataTotal;
+    /** Dropped files directory on the host. */
+    Utf8Str              m_strDropDir;
 
     ComObjPtr<Guest>     m_parent;
@@ -276,4 +288,6 @@
   , m_pvData(0)
   , m_cbData(0)
+  , m_cbDataCurrent(0)
+  , m_cbDataTotal(0)
   , m_parent(pGuest)
 {
@@ -303,6 +317,11 @@
 }
 
-int DnDGuestResponse::dataAdd(void *pvData, uint32_t cbData, uint32_t *pcbCurSize)
-{
+int DnDGuestResponse::dataAdd(const void *pvData, uint32_t cbData,
+                              uint32_t *pcbCurSize)
+{
+    AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+    AssertReturn(cbData, VERR_INVALID_PARAMETER);
+    /* pcbCurSize is optional. */
+
     int rc = VINF_SUCCESS;
 
@@ -348,7 +367,9 @@
 }
 
-int DnDGuestResponse::setProgress(unsigned uPercentage, uint32_t uState, int rcOp /* = VINF_SUCCESS */)
-{
-    LogFlowFunc(("uPercentage=%RU32, uState=%ld, rcOp=%Rrc\n", uPercentage, uState, rcOp));
+int DnDGuestResponse::setProgress(unsigned uPercentage,
+                                  uint32_t uState, int rcOp /* = VINF_SUCCESS */)
+{
+    LogFlowFunc(("uPercentage=%RU32, uState=%ld, rcOp=%Rrc\n",
+                 uPercentage, uState, rcOp));
 
     int vrc = VINF_SUCCESS;
@@ -364,5 +385,5 @@
                                                 COM_IIDOF(IGuest),
                                                 m_parent->getComponentName(),
-                                                m_parent->tr("Guest error (%Rrc)"), rcOp);
+                                                m_parent->tr("Drag'n drop guest error (%Rrc)"), rcOp);
             }
             else if (uState == DragAndDropSvc::DND_PROGRESS_CANCELLED)
@@ -390,4 +411,43 @@
 }
 
+int DnDGuestResponse::dataSetStatus(size_t cbDataAdd, size_t cbDataTotal /* = 0 */)
+{
+    if (cbDataTotal)
+    {
+        AssertMsg(m_cbDataTotal <= cbDataTotal, ("New data size size must not be smaller (%zu) than old value (%zu)\n",
+                                                 cbDataTotal, m_cbDataTotal));
+        m_cbDataTotal = cbDataTotal;
+        LogFlowFunc(("Updating total data size to: %zu\n", m_cbDataTotal));
+    }
+    AssertMsg(m_cbDataTotal, ("m_cbDataTotal must not be <= 0\n"));
+
+    m_cbDataCurrent += cbDataAdd;
+    unsigned int cPercentage = RT_MIN(m_cbDataCurrent * 100.0 / m_cbDataTotal, 100);
+
+    /** @todo Don't use anonymous enums (uint32_t). */
+    uint32_t uStatus = DragAndDropSvc::DND_PROGRESS_RUNNING;
+    if (m_cbDataCurrent >= m_cbDataTotal)
+        uStatus = DragAndDropSvc::DND_PROGRESS_COMPLETE;
+
+#ifdef DEBUG_andy
+    LogFlowFunc(("Updating transfer status (%zu/%zu), status=%ld\n",
+                 m_cbDataCurrent, m_cbDataTotal, uStatus));
+#endif
+
+    AssertMsg(m_cbDataCurrent <= m_cbDataTotal,
+              ("More data transferred (%RU32) than initially announced (%RU32)\n",
+              m_cbDataCurrent, m_cbDataTotal));
+
+    int rc = setProgress(cPercentage, uStatus);
+
+    /** @todo For now we instantly confirm the cancel. Check if the
+     *        guest should first clean up stuff itself and than really confirm
+     *        the cancel request by an extra message. */
+    if (rc == VERR_CANCELLED)
+        rc = setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED);
+
+    return rc;
+}
+
 HRESULT DnDGuestResponse::queryProgressTo(IProgress **ppProgress)
 {
@@ -503,5 +563,8 @@
 
 /* static */
-void GuestDnDPrivate::toHGCMActions(DragAndDropAction_T inDefAction, uint32_t *pOutDefAction, ComSafeArrayIn(DragAndDropAction_T, inAllowedActions), uint32_t *pOutAllowedActions)
+void GuestDnDPrivate::toHGCMActions(DragAndDropAction_T inDefAction,
+                                    uint32_t *pOutDefAction,
+                                    ComSafeArrayIn(DragAndDropAction_T, inAllowedActions),
+                                    uint32_t *pOutAllowedActions)
 {
     const com::SafeArray<DragAndDropAction_T> sfaInActions(ComSafeArrayInArg(inAllowedActions));
@@ -529,5 +592,6 @@
 DragAndDropAction_T GuestDnDPrivate::toMainAction(uint32_t uAction)
 {
-    /* For now it doesn't seems useful to allow a link action between host & guest. Maybe later! */
+    /* For now it doesn't seems useful to allow a
+     * link action between host & guest. Maybe later! */
     return (isDnDCopyAction(uAction) ? (DragAndDropAction_T)DragAndDropAction_Copy :
             isDnDMoveAction(uAction) ? (DragAndDropAction_T)DragAndDropAction_Move :
@@ -536,7 +600,9 @@
 
 /* static */
-void GuestDnDPrivate::toMainActions(uint32_t uActions, ComSafeArrayOut(DragAndDropAction_T, actions))
-{
-    /* For now it doesn't seems useful to allow a link action between host & guest. Maybe later! */
+void GuestDnDPrivate::toMainActions(uint32_t uActions,
+                                    ComSafeArrayOut(DragAndDropAction_T, actions))
+{
+    /* For now it doesn't seems useful to allow a
+     * link action between host & guest. Maybe later! */
     RTCList<DragAndDropAction_T> list;
     if (hasDnDCopyAction(uActions))
@@ -556,10 +622,14 @@
 }
 
-GuestDnD::~GuestDnD()
+GuestDnD::~GuestDnD(void)
 {
     delete d_ptr;
 }
 
-HRESULT GuestDnD::dragHGEnter(ULONG uScreenId, ULONG uX, ULONG uY, DragAndDropAction_T defaultAction, ComSafeArrayIn(DragAndDropAction_T, allowedActions), ComSafeArrayIn(IN_BSTR, formats), DragAndDropAction_T *pResultAction)
+HRESULT GuestDnD::dragHGEnter(ULONG uScreenId, ULONG uX, ULONG uY,
+                              DragAndDropAction_T defaultAction,
+                              ComSafeArrayIn(DragAndDropAction_T, allowedActions),
+                              ComSafeArrayIn(IN_BSTR, formats),
+                              DragAndDropAction_T *pResultAction)
 {
     DPTR(GuestDnD);
@@ -621,5 +691,9 @@
 }
 
-HRESULT GuestDnD::dragHGMove(ULONG uScreenId, ULONG uX, ULONG uY, DragAndDropAction_T defaultAction, ComSafeArrayIn(DragAndDropAction_T, allowedActions), ComSafeArrayIn(IN_BSTR, formats), DragAndDropAction_T *pResultAction)
+HRESULT GuestDnD::dragHGMove(ULONG uScreenId, ULONG uX, ULONG uY,
+                             DragAndDropAction_T defaultAction,
+                             ComSafeArrayIn(DragAndDropAction_T, allowedActions),
+                             ComSafeArrayIn(IN_BSTR, formats),
+                             DragAndDropAction_T *pResultAction)
 {
     DPTR(GuestDnD);
@@ -879,6 +953,12 @@
 
     const char *pcszFormat = strFormat.c_str();
-    LogFlowFunc(("strFormat=%s, uAction=0x%x\n", pcszFormat, uAction));
-    if (DnDMIMENeedsDropDir(pcszFormat, strlen(pcszFormat)))
+    bool fNeedsDropDir = DnDMIMENeedsDropDir(pcszFormat, strlen(pcszFormat));
+    LogFlowFunc(("strFormat=%s, uAction=0x%x, fNeedsDropDir=%RTbool\n",
+                 pcszFormat, uAction, fNeedsDropDir));
+
+    DnDGuestResponse *pDnD = d->response();
+    AssertPtr(pDnD);
+
+    if (fNeedsDropDir)
     {
         char szDropDir[RTPATH_MAX];
@@ -889,4 +969,6 @@
                                szDropDir, rc);
         LogFlowFunc(("Dropped files directory on the host is: %s\n", szDropDir));
+
+        pDnD->setDropDir(szDropDir);
     }
 
@@ -899,6 +981,4 @@
         paParms[i++].setUInt32(uAction);
 
-        DnDGuestResponse *pDnD = d->response();
-
         /* Reset any old data and the progress status. */
         pDnD->reset();
@@ -925,39 +1005,149 @@
     const ComObjPtr<Guest> &p = d->p;
 
-    HRESULT rc = S_OK;
-
-    DnDGuestResponse *pDnD = d->response();
-    if (pDnD)
+    HRESULT hr = S_OK;
+
+    DnDGuestResponse *pResp = d->response();
+    if (pResp)
     {
         com::SafeArray<BYTE> sfaData;
 
-        uint32_t cbData = pDnD->size();
+        size_t cbData = pResp->size();
         if (cbData)
         {
-            /* Copy the data into a safe array of bytes. */
-            const void *pvData = pDnD->data();
-            if (sfaData.resize(cbData))
-                memcpy(sfaData.raw(), pvData, cbData);
+            const void *pvData = pResp->data();
+            AssertPtr(pvData);
+
+            Utf8Str strFormat = pResp->format();
+            LogFlowFunc(("strFormat=%s, strDropDir=%s\n",
+                         strFormat.c_str(), pResp->dropDir().c_str()));
+
+            if (DnDMIMEHasFileURLs(strFormat.c_str(), strFormat.length()))
+            {
+                DnDURIList lstURI;
+                int rc2 = lstURI.RootFromURIData(pvData, cbData, 0 /* fFlags */);
+                if (RT_SUCCESS(rc2))
+                {
+                    Utf8Str strURIs = lstURI.RootToString(pResp->dropDir());
+                    if (sfaData.resize(strURIs.length()))
+                        memcpy(sfaData.raw(), strURIs.c_str(), strURIs.length());
+                    else
+                        hr = E_OUTOFMEMORY;
+                }
+                else
+                    hr = VBOX_E_IPRT_ERROR;
+
+                LogFlowFunc(("Found %zu root URIs, rc=%Rrc\n", lstURI.RootCount(), rc2));
+            }
             else
-                rc = E_OUTOFMEMORY;
+            {
+                /* Copy the data into a safe array of bytes. */
+                if (sfaData.resize(cbData))
+                    memcpy(sfaData.raw(), pvData, cbData);
+                else
+                    hr = E_OUTOFMEMORY;
+            }
         }
 
-#ifdef DEBUG_andy
-        LogFlowFunc(("Received %RU32 bytes\n", cbData));
-#endif
+        LogFlowFunc(("cbData=%zu\n", cbData));
+
         /* Detach in any case, regardless of data size. */
         sfaData.detachTo(ComSafeArrayOutArg(data));
 
         /* Delete the data. */
-        pDnD->reset();
+        pResp->reset();
     }
     else
-        rc = VBOX_E_INVALID_OBJECT_STATE;
-
+        hr = VBOX_E_INVALID_OBJECT_STATE;
+
+    LogFlowFunc(("Returning hr=%Rhrc\n", hr));
+    return hr;
+}
+
+int GuestDnD::onGHSendData(DnDGuestResponse *pResp,
+                           const void *pvData, size_t cbData,
+                           size_t cbTotalSize)
+{
+    AssertPtrReturn(pResp, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+    AssertReturn(cbData, VERR_INVALID_PARAMETER);
+    AssertReturn(cbTotalSize, VERR_INVALID_PARAMETER);
+
+    int rc = pResp->dataAdd(pvData, cbData, NULL /* Current size */);
+    if (RT_SUCCESS(rc))
+        rc = pResp->dataSetStatus(cbData, cbTotalSize);
+
+    LogFlowFuncLeaveRC(rc);
     return rc;
 }
+
+int GuestDnD::onGHSendDir(DnDGuestResponse *pResp,
+                          const char *pszPath, size_t cbPath,
+                          uint32_t fMode)
+{
+    AssertPtrReturn(pResp, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+    AssertReturn(cbPath, VERR_INVALID_PARAMETER);
+
+    LogFlowFunc(("strDir=%s, cbPath=%zu, fMode=0x%x\n",
+                 pszPath, cbPath, fMode));
+
+    int rc;
+    char *pszDir = RTPathJoinA(pResp->dropDir().c_str(), pszPath);
+    if (pszDir)
+    {
+        rc = RTDirCreateFullPath(pszDir, fMode);
+        RTStrFree(pszDir);
+    }
+    else
+        rc = VERR_NO_MEMORY;
+
+    if (RT_SUCCESS(rc))
+        rc = pResp->dataSetStatus(cbPath);
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
+
+int GuestDnD::onGHSendFile(DnDGuestResponse *pResp,
+                           const char *pszPath, size_t cbPath,
+                           void *pvData, size_t cbData, uint32_t fMode)
+{
+    AssertPtrReturn(pResp, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+    AssertReturn(cbPath, VERR_INVALID_PARAMETER);
+
+    LogFlowFunc(("strFile=%s, cbPath=%zu, fMode=0x%x\n",
+                 pszPath, cbPath, fMode));
+
+    /** @todo Add file locking between calls! */
+    int rc;
+    char *pszFile = RTPathJoinA(pResp->dropDir().c_str(), pszPath);
+    if (pszFile)
+    {
+        RTFILE hFile;
+        rc = RTFileOpen(&hFile, pszFile,
+                        RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE | RTFILE_O_WRITE);
+        if (RT_SUCCESS(rc))
+        {
+            rc = RTFileWrite(hFile, pvData, cbData,
+                             NULL /* No partial writes */);
+            RTFileClose(hFile);
+        }
+        RTStrFree(pszFile);
+    }
+    else
+        rc = VERR_NO_MEMORY;
+
+    if (RT_SUCCESS(rc))
+        rc = pResp->dataSetStatus(cbData);
+
+    LogFlowFuncLeaveRC(rc);
+    return rc;
+}
 #endif /* VBOX_WITH_DRAG_AND_DROP_GH */
 
-DECLCALLBACK(int) GuestDnD::notifyGuestDragAndDropEvent(void *pvExtension, uint32_t u32Function, void *pvParms, uint32_t cbParms)
+/* static */
+DECLCALLBACK(int) GuestDnD::notifyGuestDragAndDropEvent(void *pvExtension, uint32_t u32Function,
+                                                        void *pvParms, uint32_t cbParms)
 {
     LogFlowFunc(("pvExtension=%p, u32Function=%RU32, pvParms=%p, cbParms=%RU32\n",
@@ -967,4 +1157,7 @@
     if (!pGuest->m_pGuestDnD)
         return VINF_SUCCESS;
+
+    GuestDnD *pGuestDnD = pGuest->m_pGuestDnD;
+    AssertPtr(pGuestDnD);
 
     GuestDnDPrivate *d = static_cast<GuestDnDPrivate*>(pGuest->m_pGuestDnD->d_ptr);
@@ -984,5 +1177,7 @@
             AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGACKOPDATA) == cbParms, VERR_INVALID_PARAMETER);
             AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
+
             pResp->setDefAction(pCBData->uAction);
+
             rc = pResp->notifyAboutGuestResponse();
             break;
@@ -995,5 +1190,7 @@
             AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGREQDATADATA) == cbParms, VERR_INVALID_PARAMETER);
             AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
+
             pResp->setFormat(pCBData->pszFormat);
+
             rc = pResp->notifyAboutGuestResponse();
             break;
@@ -1006,4 +1203,5 @@
             AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA) == cbParms, VERR_INVALID_PARAMETER);
             AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
+
             rc = pResp->setProgress(pCBData->uPercentage, pCBData->uState, pCBData->rc);
             break;
@@ -1017,7 +1215,9 @@
             AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA) == cbParms, VERR_INVALID_PARAMETER);
             AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
+
             pResp->setFormat(pCBData->pszFormat);
             pResp->setDefAction(pCBData->uDefAction);
             pResp->setAllActions(pCBData->uAllActions);
+
             rc = pResp->notifyAboutGuestResponse();
             break;
@@ -1031,34 +1231,29 @@
             AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
 
-            uint32_t cbCurSize = 0;
-            rc = pResp->dataAdd(pCBData->pvData, pCBData->cbData, &cbCurSize);
-            if (RT_SUCCESS(rc))
-            {
-                /** @todo Store pCBData->cbAllSize in the guest's response struct
-                 *        if not set already. */
-                uint32_t cbTotalSize = pCBData->cbAllSize;
-                unsigned int cPercentage;
-                if (!cbTotalSize) /* Watch out for division by zero. */
-                    cPercentage = 100;
-                else
-                    cPercentage = cbCurSize * 100.0 / cbTotalSize;
-
-                /** @todo Don't use anonymous enums. */
-                uint32_t uState = DragAndDropSvc::DND_PROGRESS_RUNNING;
-                if (   cbTotalSize == cbCurSize
-                    /* Empty data? Should not happen, but anyway ... */
-                    || !cbTotalSize)
-                {
-                    uState = DragAndDropSvc::DND_PROGRESS_COMPLETE;
-                }
-
-                rc = pResp->setProgress(cPercentage, uState);
-            }
-
-            /** @todo For now we instantly confirm the cancel. Check if the
-             *        guest should first clean up stuff itself and than really confirm
-             *        the cancel request by an extra message. */
-            if (rc == VERR_CANCELLED)
-                pResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED);
+            rc = pGuestDnD->onGHSendData(pResp, pCBData->pvData, pCBData->cbData,
+                                         pCBData->cbTotalSize);
+            break;
+        }
+
+        case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
+        {
+            DragAndDropSvc::PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDIRDATA>(pvParms);
+            AssertPtr(pCBData);
+            AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER);
+            AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
+
+            rc = pGuestDnD->onGHSendDir(pResp, pCBData->pszPath, pCBData->cbPath, pCBData->fMode);
+            break;
+        }
+
+        case DragAndDropSvc::GUEST_DND_GH_SND_FILE:
+        {
+            DragAndDropSvc::PVBOXDNDCBSNDFILEDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEDATA>(pvParms);
+            AssertPtr(pCBData);
+            AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEDATA) == cbParms, VERR_INVALID_PARAMETER);
+            AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
+
+            rc = pGuestDnD->onGHSendFile(pResp, pCBData->pszFilePath, pCBData->cbFilePath,
+                                         pCBData->pvData, pCBData->cbData, pCBData->fMode);
             break;
         }
@@ -1071,5 +1266,5 @@
             AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
 
-            /* Cleanup */
+            /* Cleanup. */
             pResp->reset();
             rc = pResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc);
Index: /trunk/src/VBox/Runtime/common/misc/uri.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/misc/uri.cpp	(revision 50507)
+++ /trunk/src/VBox/Runtime/common/misc/uri.cpp	(revision 50508)
@@ -137,5 +137,5 @@
             szNum[1] = pszString[iIn++];
             szNum[2] = '\0';
-           
+
             uint8_t u8;
             rc = RTStrToUInt8Ex(szNum, NULL, 16, &u8);
@@ -654,5 +654,5 @@
     {
         uint32_t uFIntern = uFormat;
-        /* Auto is based on the current host OS. */
+        /* Auto is based on the current OS. */
         if (uFormat == URI_FILE_FORMAT_AUTO)
 #ifdef RT_OS_WINDOWS
