Index: trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-internal.h
===================================================================
--- trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-internal.h	(revision 86002)
+++ trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-internal.h	(revision 86006)
@@ -99,4 +99,22 @@
 
 /**
+ * Strucutre needed to support backwards compatbility for old(er) Guest Additions (< 6.1),
+ * which did not know the context ID concept then.
+ */
+typedef struct SHCLCLIENTLEGACYCID
+{
+    /** List node. */
+    RTLISTNODE Node;
+    /** The actual context ID. */
+    uint64_t   uCID;
+    /** Not used yet; useful to have it in the saved state though. */
+    uint32_t   enmType;
+    /** @todo Add an union here as soon as we utilize \a enmType. */
+    SHCLFORMAT uFormat;
+} SHCLCLIENTLEGACYCID;
+/** Pointer to a SHCLCLIENTLEGACYCID struct. */
+typedef SHCLCLIENTLEGACYCID *PSHCLCLIENTLEGACYCID;
+
+/**
  * Strucutre for keeping legacy state, required for keeping backwards compatibility
  * to old(er) Guest Additions.
@@ -104,8 +122,9 @@
 typedef struct SHCLCLIENTLEGACYSTATE
 {
-    /** Context ID required for an incoming VBOX_SHCL_GUEST_FN_DATA_WRITE call. Set to UINT64_MAX if not in use.
-     *  Required for:
-     *      - Guest Additions < 6.1. */
-    uint64_t idCtxWriteData;
+    /** List of context IDs (of type SHCLCLIENTLEGACYCID) for older Guest Additions which (< 6.1)
+     *  which did not know the concept of context IDs. */
+    RTLISTANCHOR lstCID;
+    /** Number of context IDs currently in \a lstCID. */
+    uint16_t     cCID;
 } SHCLCLIENTLEGACYSTATE;
 
@@ -135,6 +154,4 @@
     /** Client state flags of type SHCLCLIENTSTATE_FLAGS_. */
     uint32_t                fFlags;
-    /** Legacy cruft we have to keep to support old(er) Guest Additions. */
-    SHCLCLIENTLEGACYSTATE   Legacy;
     /** POD (plain old data) state. */
     SHCLCLIENTPODSTATE      POD;
@@ -158,4 +175,6 @@
     /** Number of allocated messages (updated atomically, not under critsect). */
     uint32_t volatile           cAllocatedMessages;
+    /** Legacy cruft we have to keep to support old(er) Guest Additions. */
+    SHCLCLIENTLEGACYSTATE       Legacy;
     /** The client's own event source.
      *  Needed for events which are not bound to a specific transfer. */
Index: trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp
===================================================================
--- trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp	(revision 86002)
+++ trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp	(revision 86006)
@@ -251,5 +251,7 @@
  * @{ */
 /** The current saved state version. */
-#define VBOX_SHCL_SAVED_STATE_VER_CURRENT   VBOX_SHCL_SAVED_STATE_VER_6_1RC1
+#define VBOX_SHCL_SAVED_STATE_VER_CURRENT   VBOX_SHCL_SAVED_STATE_LEGACY_CID
+/** Adds the legacy context ID list. */
+#define VBOX_SHCL_SAVED_STATE_LEGACY_CID    UINT32_C(0x80000005)
 /** Adds the client's POD state and client state flags.
  * @since 6.1 RC1 */
@@ -380,4 +382,12 @@
         shClSvcMsgFree(pClient, pMsg);
     }
+    /** @todo r=andy Don't we also need to reset pClient->cAllocatedMessages here? */
+
+    while (!RTListIsEmpty(&pClient->Legacy.lstCID))
+    {
+        PSHCLCLIENTLEGACYCID pCID = RTListRemoveFirst(&pClient->Legacy.lstCID, SHCLCLIENTLEGACYCID, Node);
+        RTMemFree(pCID);
+    }
+    pClient->Legacy.cCID = 0;
 }
 
@@ -556,6 +566,10 @@
     /* Assign the client ID. */
     pClient->State.uClientID = uClientID;
+
     RTListInit(&pClient->MsgQueue);
     pClient->cAllocatedMessages = 0;
+
+    RTListInit(&pClient->Legacy.lstCID);
+    pClient->Legacy.cCID = 0;
 
     LogFlowFunc(("[Client %RU32]\n", pClient->State.uClientID));
@@ -1207,4 +1221,5 @@
     SHCLEVENTID idEvent = NIL_SHCLEVENTID;
 
+    /* Generate a separate message for every (valid) format we support. */
     while (fFormats)
     {
@@ -1247,11 +1262,20 @@
                 rc = VINF_SUCCESS;
 
-                /* Save the context ID in our legacy cruft if we have to deal with old(er) Guest Additions (< 6.1.). */
+                /* Save the context ID in our legacy cruft if we have to deal with old(er) Guest Additions (< 6.1). */
                 if (!(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID))
                 {
-                    /* Only one data request at a time is supported. */
-                    AssertStmt(pClient->State.Legacy.idCtxWriteData == UINT64_MAX, rc = VERR_WRONG_ORDER);
+                    AssertStmt(pClient->Legacy.cCID < 4096, rc = VERR_TOO_MUCH_DATA);
                     if (RT_SUCCESS(rc))
-                        pClient->State.Legacy.idCtxWriteData = uCID;
+                    {
+                        PSHCLCLIENTLEGACYCID pCID = (PSHCLCLIENTLEGACYCID)RTMemAlloc(sizeof(SHCLCLIENTLEGACYCID));
+                        if (pCID)
+                        {
+                            pCID->uCID = uCID;
+                            RTListAppend(&pClient->Legacy.lstCID, &pCID->Node);
+                            pClient->Legacy.cCID++;
+                        }
+                        else
+                            rc = VERR_NO_MEMORY;
+                    }
                 }
 
@@ -1664,4 +1688,6 @@
         return VERR_ACCESS_DENIED;
 
+    const bool fReportsContextID = RT_BOOL(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID);
+
     /*
      * Digest parameters.
@@ -1670,5 +1696,5 @@
      * switch around so it's plain sailing compared to the DATA_READ message.
      */
-    ASSERT_GUEST_RETURN(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID
+    ASSERT_GUEST_RETURN(fReportsContextID
                         ? cParms == VBOX_SHCL_CPARMS_DATA_WRITE || cParms == VBOX_SHCL_CPARMS_DATA_WRITE_61B
                         : cParms == VBOX_SHCL_CPARMS_DATA_WRITE_OLD,
@@ -1684,8 +1710,55 @@
         iParm++;
     }
-    else /* Older Guest Additions (< 6.1) did not supply a context ID. Dig it out of our legacy cruft. */
-    {
-        cmdCtx.uContextID = pClient->State.Legacy.idCtxWriteData;
-        pClient->State.Legacy.idCtxWriteData = UINT64_MAX; /* Reset. */
+    else
+    {
+        /* Older Guest Additions (< 6.1) did not supply a context ID.
+         * We dig it out from our saved context ID list then a bit down below. */
+    }
+
+    if (cParms == VBOX_SHCL_CPARMS_DATA_WRITE_61B)
+    {
+        ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
+        ASSERT_GUEST_RETURN(paParms[iParm].u.uint32 == 0, VERR_INVALID_FLAGS);
+        iParm++;
+    }
+
+    SHCLFORMAT  uFormat = VBOX_SHCL_FMT_NONE;
+    uint32_t    cbData  = 0;
+    void       *pvData  = NULL;
+
+    ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /* Format bit. */
+    uFormat = paParms[iParm].u.uint32;
+    iParm++;
+    if (cParms == VBOX_SHCL_CPARMS_DATA_WRITE_61B)
+    {
+        ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /* "cbData" - duplicates buffer size. */
+        iParm++;
+    }
+    ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE); /* Data buffer */
+    pvData = paParms[iParm].u.pointer.addr;
+    cbData = paParms[iParm].u.pointer.size;
+    iParm++;
+    Assert(iParm == cParms);
+
+    /*
+     * Handle / check context ID.
+     */
+    if (!fReportsContextID) /* Do we have to deal with old(er) GAs (< 6.1) which don't support context IDs? Dig out the context ID then. */
+    {
+        PSHCLCLIENTLEGACYCID pCID = NULL;
+        RTListForEach(&pClient->Legacy.lstCID, pCID, SHCLCLIENTLEGACYCID, Node) /* Slow, but does the job for now. */
+        {
+            if (pCID->uFormat == uFormat)
+                break;
+        }
+
+        ASSERT_GUEST_MSG_RETURN(pCID != NULL, ("Context ID for format %#x not found\n", uFormat), VERR_INVALID_CONTEXT);
+        cmdCtx.uContextID = pCID->uCID;
+
+        /* Not needed anymore; clean up. */
+        Assert(pClient->Legacy.cCID);
+        pClient->Legacy.cCID--;
+        RTListNodeRemove(&pCID->Node);
+        RTMemFree(pCID);
     }
 
@@ -1695,29 +1768,4 @@
                             ("Wrong context ID: %#RX64, expected %#RX64\n", cmdCtx.uContextID, idCtxExpected),
                             VERR_INVALID_CONTEXT);
-
-    if (cParms == VBOX_SHCL_CPARMS_DATA_WRITE_61B)
-    {
-        ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
-        ASSERT_GUEST_RETURN(paParms[iParm].u.uint32 == 0, VERR_INVALID_FLAGS);
-        iParm++;
-    }
-
-    SHCLFORMAT  uFormat = VBOX_SHCL_FMT_NONE;
-    uint32_t    cbData  = 0;
-    void       *pvData  = NULL;
-
-    ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /* Format bit. */
-    uFormat = paParms[iParm].u.uint32;
-    iParm++;
-    if (cParms == VBOX_SHCL_CPARMS_DATA_WRITE_61B)
-    {
-        ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /* "cbData" - duplicates buffer size. */
-        iParm++;
-    }
-    ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE); /* Data buffer */
-    pvData = paParms[iParm].u.pointer.addr;
-    cbData = paParms[iParm].u.pointer.size;
-    iParm++;
-    Assert(iParm == cParms);
 
     /*
@@ -2124,6 +2172,4 @@
     pClientState->Transfers.enmTransferDir = SHCLTRANSFERDIR_UNKNOWN;
 #endif
-
-    pClientState->Legacy.idCtxWriteData    = UINT64_MAX;
 }
 
@@ -2213,4 +2259,18 @@
 
 #ifndef UNIT_TEST
+
+/**
+ * SSM descriptor table for the SHCLCLIENTLEGACYCID structure.
+ *
+ * @note Saving the ListEntry attribute is not necessary, as this gets used on runtime only.
+ */
+static SSMFIELD const s_aShClSSMClientLegacyCID[] =
+{
+    SSMFIELD_ENTRY(SHCLCLIENTLEGACYCID, uCID),
+    SSMFIELD_ENTRY(SHCLCLIENTLEGACYCID, enmType),
+    SSMFIELD_ENTRY(SHCLCLIENTLEGACYCID, uFormat),
+    SSMFIELD_ENTRY_TERM()
+};
+
 /**
  * SSM descriptor table for the SHCLCLIENTSTATE structure.
@@ -2315,10 +2375,9 @@
 
     /* Serialize the client's internal message queue. */
+    /** @todo r=andy Why not using pClient->cAllocatedMessages here? */
     uint32_t cMsgs = 0;
     PSHCLCLIENTMSG pMsg;
     RTListForEach(&pClient->MsgQueue, pMsg, SHCLCLIENTMSG, ListEntry)
-    {
         cMsgs += 1;
-    }
 
     rc = SSMR3PutU64(pSSM, cMsgs);
@@ -2334,4 +2393,13 @@
     }
 
+    rc = SSMR3PutU64(pSSM, pClient->Legacy.cCID);
+    AssertRCReturn(rc, rc);
+
+    PSHCLCLIENTLEGACYCID pCID;
+    RTListForEach(&pClient->Legacy.lstCID, pCID, SHCLCLIENTLEGACYCID, Node)
+    {
+        rc = SSMR3PutStructEx(pSSM, pCID, sizeof(SHCLCLIENTLEGACYCID), 0 /*fFlags*/, &s_aShClSSMClientLegacyCID[0], NULL);
+        AssertRCReturn(rc, rc);
+    }
 #else  /* UNIT_TEST */
     RT_NOREF3(u32ClientID, pvClient, pSSM);
@@ -2444,4 +2512,21 @@
 
             shClSvcMsgAdd(pClient, pMsg, true /* fAppend */);
+        }
+
+        if (lenOrVer >= VBOX_SHCL_SAVED_STATE_LEGACY_CID)
+        {
+            uint64_t cCID;
+            rc = SSMR3GetU64(pSSM, &cCID);
+            AssertRCReturn(rc, rc);
+            AssertLogRelMsgReturn(cCID < _16K, ("Too many context IDs: %u (%x)\n", cCID, cCID), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
+
+            for (uint64_t i = 0; i < cCID; i++)
+            {
+                PSHCLCLIENTLEGACYCID pCID = (PSHCLCLIENTLEGACYCID)RTMemAlloc(sizeof(SHCLCLIENTLEGACYCID));
+                AssertPtrReturn(pCID, VERR_NO_MEMORY);
+
+                SSMR3GetStructEx(pSSM, pCID, sizeof(SHCLCLIENTLEGACYCID), 0 /* fFlags */, &s_aShClSSMClientLegacyCID[0], NULL);
+                RTListAppend(&pClient->Legacy.lstCID, &pCID->Node);
+            }
         }
     }
