Index: /trunk/include/VBox/GuestHost/SharedClipboard-x11.h
===================================================================
--- /trunk/include/VBox/GuestHost/SharedClipboard-x11.h	(revision 93494)
+++ /trunk/include/VBox/GuestHost/SharedClipboard-x11.h	(revision 93495)
@@ -92,115 +92,84 @@
 {
     /** Opaque data structure describing the front-end. */
-    PSHCLCONTEXT pFrontend;
+    PSHCLCONTEXT     pFrontend;
+    /** Our callback table to use. */
+    SHCLCALLBACKS    Callbacks;
     /** Is an X server actually available? */
-    bool fHaveX11;
+    bool             fHaveX11;
     /** The X Toolkit application context structure. */
-    XtAppContext pAppContext;
-
+    XtAppContext     pAppContext;
     /** We have a separate thread to wait for window and clipboard events. */
-    RTTHREAD Thread;
+    RTTHREAD         Thread;
     /** Flag indicating that the thread is in a started state. */
-    bool fThreadStarted;
-
+    bool             fThreadStarted;
     /** The X Toolkit widget which we use as our clipboard client.  It is never made visible. */
-    Widget pWidget;
-
+    Widget           pWidget;
     /** Should we try to grab the clipboard on startup? */
-    bool fGrabClipboardOnStart;
-
+    bool             fGrabClipboardOnStart;
     /** The best text format X11 has to offer, as an index into the formats table. */
-    SHCLX11FMTIDX idxFmtText;
+    SHCLX11FMTIDX    idxFmtText;
     /** The best bitmap format X11 has to offer, as an index into the formats table. */
-    SHCLX11FMTIDX idxFmtBmp;
+    SHCLX11FMTIDX    idxFmtBmp;
     /** The best HTML format X11 has to offer, as an index into the formats table. */
-    SHCLX11FMTIDX idxFmtHTML;
+    SHCLX11FMTIDX    idxFmtHTML;
 #ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
     /** The best HTML format X11 has to offer, as an index into the formats table. */
-    SHCLX11FMTIDX   idxFmtURI;
+    SHCLX11FMTIDX    idxFmtURI;
 # ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
     /** HTTP transfer context data. */
-    SHCLHTTPCONTEXT HttpCtx;
+    SHCLHTTPCONTEXT  HttpCtx;
 # endif
 #endif
     /** What kind of formats does VBox have to offer? */
-    SHCLFORMATS vboxFormats;
+    SHCLFORMATS      vboxFormats;
     /** Cache of the last unicode data that we received. */
-    void *pvUnicodeCache;
+    void            *pvUnicodeCache;
     /** Size of the unicode data in the cache. */
-    uint32_t cbUnicodeCache;
+    uint32_t         cbUnicodeCache;
     /** When we wish the clipboard to exit, we have to wake up the event
      * loop.  We do this by writing into a pipe.  This end of the pipe is
      * the end that another thread can write to. */
-    int wakeupPipeWrite;
+    int              wakeupPipeWrite;
     /** The reader end of the pipe. */
-    int wakeupPipeRead;
+    int              wakeupPipeRead;
     /** A pointer to the XFixesSelectSelectionInput function. */
     void (*fixesSelectInput)(Display *, Window, Atom, unsigned long);
     /** The first XFixes event number. */
-    int fixesEventBase;
+    int              fixesEventBase;
 #ifdef VBOX_WITH_SHARED_CLIPBOARD_XT_BUSY
     /** XtGetSelectionValue on some versions of libXt isn't re-entrant
      * so block overlapping requests on this flag. */
-    bool fXtBusy;
+    bool             fXtBusy;
     /** If a request is blocked on the previous flag, set this flag to request
      * an update later - the first callback should check and clear this flag
      * before processing the callback event. */
-    bool fXtNeedsUpdate;
+    bool             fXtNeedsUpdate;
 #endif
 } SHCLX11CTX, *PSHCLX11CTX;
+
+/**
+ * Structure for keeping a X11 read data request.
+ */
+typedef struct _SHCLX11READDATAREQ
+{
+    /** Actual read request to handle. */
+    CLIPREADCBREQ *pReq;
+    /** Result code of the operation on completion. */
+    int            rcCompletion;
+} SHCLX11READDATAREQ;
+/** Pointer to a send data request. */
+typedef SHCLX11READDATAREQ *PSHCLX11READDATAREQ;
 
 /** @name Shared Clipboard APIs for X11.
  * @{
  */
-int ShClX11Init(PSHCLX11CTX pCtx, PSHCLCONTEXT pParent, bool fHeadless);
+int ShClX11Init(PSHCLX11CTX pCtx, PSHCLCALLBACKS pCallbacks, PSHCLCONTEXT pParent, bool fHeadless);
 void ShClX11Destroy(PSHCLX11CTX pCtx);
 int ShClX11ThreadStart(PSHCLX11CTX pCtx, bool grab);
+int ShClX11ThreadStartEx(PSHCLX11CTX pCtx, const char *pszName, bool fGrab);
 int ShClX11ThreadStop(PSHCLX11CTX pCtx);
 int ShClX11ReportFormatsToX11(PSHCLX11CTX pCtx, SHCLFORMATS vboxFormats);
 int ShClX11ReadDataFromX11(PSHCLX11CTX pCtx, SHCLFORMATS vboxFormat, CLIPREADCBREQ *pReq);
-/** @} */
-
-/** @name Shared Clipboard callbacks which have to be implemented by tools using the X11
- *        clipboard, e.g. VBoxClient (on guest side) or the X11 host service backend.
- * @{
- */
-/**
- * Callback for reporting supported formats of current clipboard data from X11 to VBox.
- *
- * @note   Runs in Xt event thread.
- *
- * @param  pCtx                 Opaque context pointer for the glue code.
- * @param  fFormats             The formats available.
- */
-DECLCALLBACK(void) ShClX11ReportFormatsCallback(PSHCLCONTEXT pCtx, SHCLFORMATS fFormats);
-
-/**
- * Callback for requesting clipboard data for X11.
- * The function will be invoked for every single target the clipboard requests.
- *
- * @note Runs in Xt event thread.
- *
- * @returns VBox status code. VERR_NO_DATA if no data available.
- * @param   pCtx                Pointer to the host clipboard structure.
- * @param   uFmt                The format in which the data should be transferred
- *                              (VBOX_SHCL_FMT_XXX).
- * @param   ppv                 Returns an allocated buffer with data read from the guest on success.
- *                              Needs to be free'd with RTMemFree() by the caller.
- * @param   pcb                 Returns the amount of data read (in bytes) on success.
- */
-DECLCALLBACK(int) ShClX11RequestDataCallback(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb);
-
-/**
- * Callback for reporting that clipboard data from X11 is available.
- *
- * @param  pCtx                 Our context information.
- * @param  rcCompletion         The completion status of the request.
- * @param  pReq                 The request structure that we passed in when we started
- *                              the request.  We RTMemFree() this in this function.
- * @param  pv                   The clipboard data returned from X11 if the request succeeded (see @a rcCompletion).
- * @param  cb                   The size of the data in @a pv.
- */
-DECLCALLBACK(void) ShClX11ReportDataCallback(PSHCLCONTEXT pCtx, int rcCompletion,
-                                             CLIPREADCBREQ *pReq, void *pv, uint32_t cb);
+void ShClX11SetCallbacks(PSHCLX11CTX pCtx, PSHCLCALLBACKS pCallbacks);
 /** @} */
 
Index: /trunk/include/VBox/GuestHost/SharedClipboard.h
===================================================================
--- /trunk/include/VBox/GuestHost/SharedClipboard.h	(revision 93494)
+++ /trunk/include/VBox/GuestHost/SharedClipboard.h	(revision 93495)
@@ -232,4 +232,107 @@
 typedef SHCLCONTEXT *PSHCLCONTEXT;
 
+/**
+ * @name Shared Clipboard callback table.
+ *
+ * This table gets used by
+ *   - the backends on the host (where required)
+ *   - guest side implementations (e.g. VBoxClient)
+ *   - by the underlying core code (e.g. X11 backend -> X11 common code -> callback)
+ *
+ * Some clipboard mechanisms (e.g. X11) require asynchronous and/or event-driven handling
+ * of clipboard data, making it hard to control our program flow when testing stuff.
+ *
+ * So overriding required callbacks on runtime for testing purposes makes this approach much
+ * more flexible without implementing separate code paths for production code and test units.
+ *
+ * @{
+ */
+typedef struct _SHCLCALLBACKS
+{
+    /**
+     * Callback for reporting supported clipoard formats of current clipboard data.
+     *
+     * @note On X11:
+     *         Runs in Xt event thread for the X11 code.
+     *
+     * @returns VBox status code.
+     * @param   pCtx            Opaque context pointer for the glue code.
+     * @param   fFormats        The formats available.
+     */
+    DECLCALLBACKMEMBER(int, pfnReportFormats, (PSHCLCONTEXT pCtx, SHCLFORMATS fFormats, void *pvUser));
+
+    /**
+     * Callback for reading data from the clipboard.
+     * Optional and can be NULL.
+     *
+     * @note Used for testing X11 clipboard code.
+     *
+     * @returns VBox status code.
+     * @param   pCtx            Opaque context pointer for the glue code.
+     * @param   uFmt            The format in which the data should be read
+     *                          (VBOX_SHCL_FMT_XXX).
+     * @param   ppv             Returns an allocated buffer with data from on success.
+     *                          Needs to be free'd with RTMemFree() by the caller.
+     * @param   pcb             Returns the amount of data read (in bytes) on success.
+     * @param   pvUser          Implementation-dependent pointer to data for fullfilling the request.
+     *                          Optional and can be NULL.
+     */
+    DECLCALLBACKMEMBER(int, pfnOnClipboardRead, (PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, size_t *pcb, void *pvUser));
+
+    /**
+     * Callback for writing data to the clipboard.
+     * Optional and can be NULL.
+     *
+     * @note Used for testing X11 clipboard code.
+     *
+     * @returns VBox status code.
+     * @param   pCtx            Opaque context pointer for the glue code.
+     * @param   uFmt            The format in which the data should be written as
+     *                          (VBOX_SHCL_FMT_XXX).
+     * @param   pv              The clipboard data to write.
+     * @param   cb              The size of the data in @a pv.
+     * @param   pvUser          Implementation-dependent pointer to data for fullfilling the request.
+     *                          Optional and can be NULL.
+     */
+    DECLCALLBACKMEMBER(int, pfnOnClipboardWrite, (PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void *pv, size_t cb, void *pvUser));
+
+    /**
+     * Callback for requesting clipboard data from the source.
+     *
+     * @note On X11:
+     *         The function will be invoked for every single target the clipboard requests.
+     *         Runs in Xt event thread for the X11 code.
+     *
+     * @returns VBox status code. VERR_NO_DATA if no data available.
+     * @param   pCtx            Opaque context pointer for the glue code.
+     * @param   uFmt            The format in which the data should be transferred
+     *                          (VBOX_SHCL_FMT_XXX).
+     * @param   ppv             Returns an allocated buffer with data read from the guest on success.
+     *                          Needs to be free'd with RTMemFree() by the caller.
+     * @param   pcb             Returns the amount of data read (in bytes) on success.
+     * @param   pvUser          Implementation-dependent pointer to data for fullfilling the request.
+     *                          Optional and can be NULL.
+     *                          On X11: Of type PSHCLX11READDATAREQ; We RTMemFree() this in this function.
+     */
+    DECLCALLBACKMEMBER(int, pfnOnRequestDataFromSource, (PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser));
+
+    /**
+     * Callback for sending clipboard data to the destination.
+     *
+     * @note On X11:
+     *         (see @a rcCompletion)
+     *
+     * @returns VBox status code.
+     * @param   pCtx            Opaque context pointer for the glue code.
+     * @param   pv              The clipboard data returned if the request succeeded.
+     * @param   cb              The size of the data in @a pv.
+     * @param   pvUser          Implementation-dependent pointer to data for fullfilling the request.
+     *                          Optional and can be NULL.
+     */
+    DECLCALLBACKMEMBER(int, pfnOnSendDataToDest, (PSHCLCONTEXT pCtx, void *pv, uint32_t cb, void *pvUser));
+} SHCLCALLBACKS;
+typedef SHCLCALLBACKS *PSHCLCALLBACKS;
+/** @} */
+
 /** Opaque request structure for X11 clipboard data.
  * @{ */
Index: /trunk/src/VBox/Additions/x11/VBoxClient/clipboard.cpp
===================================================================
--- /trunk/src/VBox/Additions/x11/VBoxClient/clipboard.cpp	(revision 93494)
+++ /trunk/src/VBox/Additions/x11/VBoxClient/clipboard.cpp	(revision 93495)
@@ -177,5 +177,5 @@
     LogFlowFuncEnter();
 
-    int rc = ShClX11Init(&g_Ctx.X11, &g_Ctx, false /* fHeadless */);
+    int rc = ShClX11Init(&g_Ctx.X11, NULL /* pCallbacks */, &g_Ctx, false /* fHeadless */);
     if (RT_SUCCESS(rc))
     {
Index: /trunk/src/VBox/GuestHost/SharedClipboard/clipboard-x11.cpp
===================================================================
--- /trunk/src/VBox/GuestHost/SharedClipboard/clipboard-x11.cpp	(revision 93494)
+++ /trunk/src/VBox/GuestHost/SharedClipboard/clipboard-x11.cpp	(revision 93495)
@@ -449,5 +449,5 @@
 #endif
 
-    ShClX11ReportFormatsCallback(pCtx->pFrontend, vboxFmt);
+    pCtx->Callbacks.pfnReportFormats(pCtx->pFrontend, vboxFmt, NULL /* pvUser */);
 }
 
@@ -742,5 +742,6 @@
     unsigned cFormats = *pcLen;
 
-    LogRel2(("Shared Clipboard: %u formats were found\n", cFormats));
+    LogRel2(("Shared Clipboard: Querying X11 formats ...\n", cFormats));
+    LogRel2(("Shared Clipboard: %u X11 formats were found\n", cFormats));
 
     SHCLX11FMTIDX *paIdxFmt = NULL;
@@ -761,5 +762,5 @@
             {
                 char *pszName = XGetAtomName(XtDisplay(widget), pAtoms[i]);
-                LogRel2(("Shared Clipboard: Found target '%s'\n", pszName));
+                LogRel2(("Shared Clipboard: Found X11 format '%s'\n", pszName));
                 XFree(pszName);
             }
@@ -783,5 +784,5 @@
 #if !defined(TESTCASE)
             if (paIdxFmt[i] != SHCLX11FMT_INVALID)
-                LogRel2(("Shared Clipboard: Reporting format '%s'\n", g_aFormats[paIdxFmt[i]].pcszAtom));
+                LogRel2(("Shared Clipboard: Reporting X11 format '%s'\n", g_aFormats[paIdxFmt[i]].pcszAtom));
 #endif
         }
@@ -1150,18 +1151,42 @@
 
 /**
+ * Sets the callback table, internal version.
+ *
+ * @param   pCtx                The clipboard context.
+ * @param   pCallbacks          Callback table to set. If NULL, the current callback table will be cleared.
+ */
+static void shClX11SetCallbacksInternal(PSHCLX11CTX pCtx, PSHCLCALLBACKS pCallbacks)
+{
+    if (pCallbacks)
+    {
+        memcpy(&pCtx->Callbacks, pCallbacks, sizeof(SHCLCALLBACKS));
+    }
+    else
+        RT_ZERO(pCtx->Callbacks);
+}
+
+/**
+ * Sets the callback table.
+ *
+ * @param   pCtx                The clipboard context.
+ * @param   pCallbacks          Callback table to set. If NULL, the current callback table will be cleared.
+ */
+void ShClX11SetCallbacks(PSHCLX11CTX pCtx, PSHCLCALLBACKS pCallbacks)
+{
+    shClX11SetCallbacksInternal(pCtx, pCallbacks);
+}
+
+/**
  * Initializes a X11 context of the Shared Clipboard.
  *
  * @returns VBox status code.
  * @param   pCtx                The clipboard context to initialize.
+ * @param   pCallbacks          Callback table to use.
  * @param   pParent             Parent context to use.
  * @param   fHeadless           Whether the code runs in a headless environment or not.
  */
-int ShClX11Init(PSHCLX11CTX pCtx, PSHCLCONTEXT pParent, bool fHeadless)
-{
-    AssertPtrReturn(pCtx,    VERR_INVALID_POINTER);
-#if !defined(SMOKETEST) && !defined(TESTCASE)
-    /* Smoktests / Testcases don't have a (valid) parent. */
-    AssertPtrReturn(pParent, VERR_INVALID_POINTER);
-#endif
+int ShClX11Init(PSHCLX11CTX pCtx, PSHCLCALLBACKS pCallbacks, PSHCLCONTEXT pParent, bool fHeadless)
+{
+    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
 
     LogFlowFunc(("pCtx=%p\n", pCtx));
@@ -1182,4 +1207,7 @@
     }
 
+    /* Install given callbacks. */
+    shClX11SetCallbacksInternal(pCtx, pCallbacks);
+
     pCtx->fHaveX11       = !fHeadless;
     pCtx->pFrontend      = pParent;
@@ -1237,11 +1265,12 @@
 #ifndef TESTCASE
 /**
- * Starts our own Xt even thread for handling Shared Clipboard messages.
+ * Starts our own Xt even thread for handling Shared Clipboard messages, extended version.
  *
  * @returns VBox status code.
  * @param   pCtx                The X11 clipboard context to use.
+ * @param   pszName             Thread name to use.
  * @param   fGrab               Whether we should try to grab the shared clipboard at once.
  */
-int ShClX11ThreadStart(PSHCLX11CTX pCtx, bool fGrab)
+int ShClX11ThreadStartEx(PSHCLX11CTX pCtx, const char *pszName, bool fGrab)
 {
     AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
@@ -1284,5 +1313,5 @@
 
         rc = RTThreadCreate(&pCtx->Thread, clipThreadMain, pCtx, 0,
-                            RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLX11");
+                            RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, pszName);
         if (RT_SUCCESS(rc))
             rc = RTThreadUserWait(pCtx->Thread, RT_MS_30SEC /* msTimeout */);
@@ -1306,4 +1335,16 @@
     LogFlowFuncLeaveRC(rc);
     return rc;
+}
+
+/**
+ * Starts our own Xt even thread for handling Shared Clipboard messages.
+ *
+ * @returns VBox status code.
+ * @param   pCtx                The X11 clipboard context to use.
+ * @param   fGrab               Whether we should try to grab the shared clipboard at once.
+ */
+int ShClX11ThreadStart(PSHCLX11CTX pCtx, bool fGrab)
+{
+    return ShClX11ThreadStartEx(pCtx, "SHCLX11", fGrab);
 }
 
@@ -1453,8 +1494,10 @@
     {
         if (pCtx->pvUnicodeCache == NULL) /** @todo r=andy Using string cache here? */
-            rc = ShClX11RequestDataCallback(pCtx->pFrontend, uFmt,
-                                            &pCtx->pvUnicodeCache,
-                                            &pCtx->cbUnicodeCache);
-        if (RT_SUCCESS(rc))
+            rc = pCtx->Callbacks.pfnOnRequestDataFromSource(pCtx->pFrontend, uFmt, &pCtx->pvUnicodeCache, &pCtx->cbUnicodeCache,
+                                                            NULL /* pvUser */);
+        if (   RT_SUCCESS(rc)
+            /* Catch misbehaving callbacks. */
+            && pCtx->pvUnicodeCache
+            && pCtx->cbUnicodeCache)
         {
             pv = RTMemDup(pCtx->pvUnicodeCache, pCtx->cbUnicodeCache);
@@ -1466,5 +1509,5 @@
     }
     else
-        rc = ShClX11RequestDataCallback(pCtx->pFrontend, uFmt, &pv, &cb);
+        rc = pCtx->Callbacks.pfnOnRequestDataFromSource(pCtx->pFrontend, uFmt, &pv, &cb, NULL /* pvUser */);
 
 
@@ -1781,4 +1824,7 @@
         return False;
 
+    XSelectionRequestEvent* req =
+        XtGetSelectionRequest(widget, *atomSelection, (XtRequestId)NULL);
+
     /* Is this the rigt selection (clipboard) we were asked for? */
     if (!clipIsSupportedSelectionType(pCtx, *atomSelection))
@@ -1793,6 +1839,20 @@
                                   pValReturn, pcLenReturn, piFormatReturn);
 
-    LogFlowFunc(("returning %RTbool, rc=%Rrc\n", RT_SUCCESS(rc), rc));
+
+    LogFlowFunc(("returning pVBoxWnd=%#x, ownerWnd=%#x, reqWnd=%#x, %RTbool, rc=%Rrc\n",
+                 XtWindow(pCtx->pWidget), req->owner, req->requestor, RT_SUCCESS(rc), rc));
     return RT_SUCCESS(rc) ? True : False;
+}
+
+static void clipXtConvertSelectionProcLose(Widget widget, Atom *atomSelection)
+{
+    RT_NOREF(widget, atomSelection);
+    LogFlowFuncEnter();
+}
+
+static void clipXtConvertSelectionProcDone(Widget widget, Atom *atomSelection, Atom *atomTarget)
+{
+    RT_NOREF(widget, atomSelection, atomTarget);
+    LogFlowFuncEnter();
 }
 
@@ -1834,6 +1894,8 @@
     LogFlowFuncEnter();
 
+    /** @ŧodo r=andy The docs say: "the value CurrentTime is not acceptable" here!? */
     if (XtOwnSelection(pCtx->pWidget, clipGetAtom(pCtx, "CLIPBOARD"),
-                       CurrentTime, clipXtConvertSelectionProc, NULL, 0))
+                       CurrentTime,
+                       clipXtConvertSelectionProc, clipXtConvertSelectionProcLose, clipXtConvertSelectionProcDone))
     {
         pCtx->vboxFormats = uFormats;
@@ -1874,5 +1936,10 @@
     RTMemFree(pFormats);
 
-    LogFlowFunc (("fFormats=0x%x\n", fFormats));
+#ifdef LOG_ENABLED
+    char *pszFmts = ShClFormatsToStrA(fFormats);
+    AssertPtrReturnVoid(pszFmts);
+    LogRel2(("Shared Clipboard: Reported available VBox formats %s to X11\n", pszFmts));
+    RTStrFree(pszFmts);
+#endif
 
     clipInvalidateClipboardCache(pCtx);
@@ -1906,5 +1973,4 @@
         pFormats->pCtx    = pCtx;
         pFormats->Formats = uFormats;
-
         rc = clipThreadScheduleCall(pCtx, ShClX11ReportFormatsToX11Worker,
                                     (XtPointer)pFormats);
@@ -1969,5 +2035,4 @@
             {
                 size_t cwDst;
-
                 /* If we are given broken UTF-8, we treat it as Latin1. */ /** @todo BUGBUG Is this acceptable? */
                 if (RT_SUCCESS(RTStrValidateEncodingEx((char *)pvSrc, cbSrc, 0)))
@@ -2151,5 +2216,10 @@
                 g_aFormats[pReq->idxFmtX11].pcszAtom, pReq->idxFmtX11, pReq->uFmtVBox, rc));
 
-    ShClX11ReportDataCallback(pReq->pCtx->pFrontend, rc, pReq->pReq, pvDst, cbDst);
+    SHCLX11READDATAREQ SendData;
+    RT_ZERO(SendData);
+    SendData.pReq         = pReq->pReq;
+    SendData.rcCompletion = rc;
+
+    pCtx->Callbacks.pfnOnSendDataToDest(pReq->pCtx->pFrontend, pvDst, cbDst, &SendData);
 
     RTMemFree(pvDst);
@@ -2175,5 +2245,23 @@
         clipConvertDataFromX11Worker(pClient, NULL, 0);
     else
-        clipConvertDataFromX11Worker(pClient, pvSrc, (*pcLen) * (*piFormat) / 8);
+    {
+        CLIPREADX11CBREQ *pReq = (CLIPREADX11CBREQ *)pClient;
+        if (pReq->pCtx->Callbacks.pfnOnClipboardRead)
+        {
+            void  *pvData = NULL;
+            size_t cbData = 0;
+            int rc = pReq->pCtx->Callbacks.pfnOnClipboardRead(pReq->pCtx->pFrontend, pReq->uFmtVBox, &pvData, &cbData, NULL);
+            if (RT_SUCCESS(rc))
+            {
+                /* Feed to conversion worker. */
+                clipConvertDataFromX11Worker(pClient, pvData, cbData);
+                RTMemFree(pvData);
+            }
+            else
+                clipConvertDataFromX11Worker(pClient, NULL, 0);
+        }
+        else /* Call with current data provided by X (default). */
+            clipConvertDataFromX11Worker(pClient, pvSrc, (*pcLen) * (*piFormat) / 8);
+    }
 
     XtFree((char *)pvSrc);
@@ -2209,6 +2297,7 @@
     CLIPREADX11CBREQ *pReq = (CLIPREADX11CBREQ *)pvUserData;
     SHCLX11CTX       *pCtx = pReq->pCtx;
-
-    LogFlowFunc(("pReq->mFormat = %02x\n", pReq->uFmtVBox));
+    AssertPtrReturnVoid(pCtx);
+
+    LogFlowFunc(("pReq->uFmtVBox=%#x, idxFmtX11=%#x\n", pReq->uFmtVBox, pReq->idxFmtX11));
 
     int rc = VERR_NO_DATA; /* VBox thinks we have data and we don't. */
@@ -2274,6 +2363,10 @@
         /* The clipboard callback was never scheduled, so we must signal
          * that the request processing is finished and clean up ourselves. */
-        ShClX11ReportDataCallback(pReq->pCtx->pFrontend, rc, pReq->pReq,
-                                  NULL /* pv */ ,0 /* cb */);
+        SHCLX11READDATAREQ SendData;
+        RT_ZERO(SendData);
+        SendData.pReq         = pReq->pReq;
+        SendData.rcCompletion = rc;
+
+        pCtx->Callbacks.pfnOnSendDataToDest(pReq->pCtx->pFrontend, NULL /* pv */ ,0 /* cb */, &SendData);
         RTMemFree(pReq);
     }
@@ -2289,5 +2382,5 @@
  * @retval  VERR_NOT_IMPLEMENTED if the format is not implemented.
  * @param   pCtx                Context data for the clipboard backend.
- * @param   Format              The format that the VBox would like to receive the data in.
+ * @param   uFmt                The format that the VBox would like to receive the data in.
  * @param   pReq                Read callback request to use. Will be free'd in the callback on success.
  *                              Otherwise the caller has to free this again on error.
@@ -2295,8 +2388,7 @@
  * @note   We allocate a request structure which must be freed by the worker.
  */
-int ShClX11ReadDataFromX11(PSHCLX11CTX pCtx, SHCLFORMAT Format, CLIPREADCBREQ *pReq)
+int ShClX11ReadDataFromX11(PSHCLX11CTX pCtx, SHCLFORMAT uFmt, CLIPREADCBREQ *pReq)
 {
     AssertPtrReturn(pReq, VERR_INVALID_POINTER);
-
     /*
      * Immediately return if we are not connected to the X server.
@@ -2311,5 +2403,5 @@
     {
         pX11Req->pCtx     = pCtx;
-        pX11Req->uFmtVBox = Format;
+        pX11Req->uFmtVBox = uFmt;
         pX11Req->pReq     = pReq;
 
Index: /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-darwin.cpp
===================================================================
--- /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-darwin.cpp	(revision 93494)
+++ /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-darwin.cpp	(revision 93495)
@@ -120,7 +120,7 @@
 
 
-int ShClBackendInit(VBOXHGCMSVCFNTABLE *pTable)
-{
-    RT_NOREF(pTable);
+int ShClBackendInit(PSHCLBACKEND pBackend, VBOXHGCMSVCFNTABLE *pTable)
+{
+    RT_NOREF(pBackend, pTable);
     g_ctx.fTerminate = false;
 
@@ -139,6 +139,8 @@
 }
 
-void ShClBackendDestroy(void)
-{
+void ShClBackendDestroy(PSHCLBACKEND pBackend)
+{
+    RT_NOREF(pBackend);
+
     /*
      * Signal the termination of the polling thread and wait for it to respond.
@@ -158,7 +160,7 @@
 }
 
-int ShClBackendConnect(PSHCLCLIENT pClient, bool fHeadless)
-{
-    RT_NOREF(fHeadless);
+int ShClBackendConnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, bool fHeadless)
+{
+    RT_NOREF(pBackend, fHeadless);
 
     if (g_ctx.pClient != NULL)
@@ -178,6 +180,8 @@
 }
 
-int ShClBackendSync(PSHCLCLIENT pClient)
-{
+int ShClBackendSync(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
+{
+    RT_NOREF(pBackend);
+
     /* Sync the host clipboard content with the client. */
     ShClSvcLock();
@@ -190,6 +194,8 @@
 }
 
-int ShClBackendDisconnect(PSHCLCLIENT pClient)
-{
+int ShClBackendDisconnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
+{
+    RT_NOREF(pBackend);
+
     ShClSvcLock();
 
@@ -201,6 +207,8 @@
 }
 
-int ShClBackendFormatAnnounce(PSHCLCLIENT pClient, SHCLFORMATS fFormats)
-{
+int ShClBackendFormatAnnounce(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, SHCLFORMATS fFormats)
+{
+    RT_NOREF(pBackend);
+
     LogFlowFunc(("fFormats=%02X\n", fFormats));
 
@@ -247,5 +255,5 @@
 }
 
-int ShClBackendReadData(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT fFormat,
+int ShClBackendReadData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT fFormat,
                         void *pvData, uint32_t cbData, uint32_t *pcbActual)
 {
@@ -255,5 +263,5 @@
     AssertPtrReturn(pcbActual, VERR_INVALID_POINTER);
 
-    RT_NOREF(pCmdCtx);
+    RT_NOREF(pBackend, pCmdCtx);
 
     ShClSvcLock();
@@ -271,7 +279,7 @@
 }
 
-int ShClBackendWriteData(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT fFormat, void *pvData, uint32_t cbData)
-{
-    RT_NOREF(pCmdCtx);
+int ShClBackendWriteData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT fFormat, void *pvData, uint32_t cbData)
+{
+    RT_NOREF(pBackend, pCmdCtx);
 
     LogFlowFuncEnter();
@@ -289,37 +297,37 @@
 #ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
 
-int ShClBackendTransferReadDir(PSHCLCLIENT pClient, PSHCLDIRDATA pDirData)
-{
-    RT_NOREF(pClient, pDirData);
-    return VERR_NOT_IMPLEMENTED;
-}
-
-int ShClBackendTransferWriteDir(PSHCLCLIENT pClient, PSHCLDIRDATA pDirData)
-{
-    RT_NOREF(pClient, pDirData);
-    return VERR_NOT_IMPLEMENTED;
-}
-
-int ShClBackendTransferReadFileHdr(PSHCLCLIENT pClient, PSHCLFILEHDR pFileHdr)
-{
-    RT_NOREF(pClient, pFileHdr);
-    return VERR_NOT_IMPLEMENTED;
-}
-
-int ShClBackendTransferWriteFileHdr(PSHCLCLIENT pClient, PSHCLFILEHDR pFileHdr)
-{
-    RT_NOREF(pClient, pFileHdr);
-    return VERR_NOT_IMPLEMENTED;
-}
-
-int ShClBackendTransferReadFileData(PSHCLCLIENT pClient, PSHCLFILEDATA pFileData)
-{
-    RT_NOREF(pClient, pFileData);
-    return VERR_NOT_IMPLEMENTED;
-}
-
-int ShClBackendTransferWriteFileData(PSHCLCLIENT pClient, PSHCLFILEDATA pFileData)
-{
-    RT_NOREF(pClient, pFileData);
+int ShClBackendTransferReadDir(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLDIRDATA pDirData)
+{
+    RT_NOREF(pBackend, pClient, pDirData);
+    return VERR_NOT_IMPLEMENTED;
+}
+
+int ShClBackendTransferWriteDir(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLDIRDATA pDirData)
+{
+    RT_NOREF(pBackend, pClient, pDirData);
+    return VERR_NOT_IMPLEMENTED;
+}
+
+int ShClBackendTransferReadFileHdr(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLFILEHDR pFileHdr)
+{
+    RT_NOREF(pBackend, pClient, pFileHdr);
+    return VERR_NOT_IMPLEMENTED;
+}
+
+int ShClBackendTransferWriteFileHdr(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLFILEHDR pFileHdr)
+{
+    RT_NOREF(pBackend, pClient, pFileHdr);
+    return VERR_NOT_IMPLEMENTED;
+}
+
+int ShClBackendTransferReadFileData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLFILEDATA pFileData)
+{
+    RT_NOREF(pBackend, pClient, pFileData);
+    return VERR_NOT_IMPLEMENTED;
+}
+
+int ShClBackendTransferWriteFileData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLFILEDATA pFileData)
+{
+    RT_NOREF(pBackend, pClient, pFileData);
     return VERR_NOT_IMPLEMENTED;
 }
Index: /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-internal.h
===================================================================
--- /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-internal.h	(revision 93494)
+++ /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-internal.h	(revision 93495)
@@ -139,6 +139,8 @@
     struct SHCLCLIENTSTATE *pPrev;
 
+    /** Backend-dependent opaque context structure.
+     *  This contains data only known to a certain backend implementation.
+     *  Optional and can be NULL. */
     SHCLCONTEXT            *pCtx;
-
     /** The client's HGCM ID. Not related to the session ID below! */
     uint32_t                uClientID;
@@ -177,4 +179,8 @@
 #endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
 
+/** Prototypes for the Shared Clipboard backend. */
+struct SHCLBACKEND;
+typedef SHCLBACKEND *PSHCLBACKEND;
+
 /**
  * Structure for keeping data per (connected) HGCM client.
@@ -182,4 +188,7 @@
 typedef struct _SHCLCLIENT
 {
+    /** Pointer to associated backend, if any.
+     *  Might be NULL if not being used. */
+    PSHCLBACKEND                pBackend;
     /** General client state data. */
     SHCLCLIENTSTATE             State;
@@ -296,4 +305,5 @@
 int ShClSvcGuestDataSignal(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat, void *pvData, uint32_t cbData);
 int ShClSvcHostReportFormats(PSHCLCLIENT pClient, SHCLFORMATS fFormats);
+PSHCLBACKEND ShClSvcGetBackend(void);
 uint32_t ShClSvcGetMode(void);
 bool ShClSvcGetHeadless(void);
@@ -311,20 +321,35 @@
 /** @} */
 
-
-/** @name Platform-dependent implementations for the Shared Clipboard host service, called *only* by the host service.
+/** @name Platform-dependent implementations for the Shared Clipboard host service ("backends"),
+ *        called *only* by the host service.
  * @{
  */
 /**
+ * Structure for keeping Shared Clipboard backend instance data.
+ */
+typedef struct SHCLBACKEND
+{
+    /** Callback table to use.
+     *  Some callbacks might be optional and therefore NULL -- see the table for more details. */
+    SHCLCALLBACKS Callbacks;
+} SHCLBACKEND;
+/** Pointer to a Shared Clipboard backend. */
+typedef SHCLBACKEND *PSHCLBACKEND;
+
+/**
  * Called on initialization.
  *
+ * @param   pBackend    Shared Clipboard backend to initialize.
  * @param   pTable      The HGCM service call and parameter table.  Mainly for
  *                      adjusting the limits.
  */
-int ShClBackendInit(VBOXHGCMSVCFNTABLE *pTable);
+int ShClBackendInit(PSHCLBACKEND pBackend, VBOXHGCMSVCFNTABLE *pTable);
 
 /**
  * Called on destruction.
- */
-void ShClBackendDestroy(void);
+ *
+ * @param   pBackend    Shared Clipboard backend to destroy.
+ */
+void ShClBackendDestroy(PSHCLBACKEND pBackend);
 
 /**
@@ -332,8 +357,19 @@
  *
  * @returns VBox status code.
+ * @param   pBackend            Shared Clipboard backend to set callbacks for.
+ * @param   pCallbacks          Backend callbacks to use.
+ *                              When NULL is specified, the backend's default callbacks are being used.
+ */
+void ShClBackendSetCallbacks(PSHCLBACKEND pBackend, PSHCLCALLBACKS pCallbacks);
+
+/**
+ * Called when a new HGCM client connects.
+ *
+ * @returns VBox status code.
+ * @param   pBackend            Shared Clipboard backend to connect to.
  * @param   pClient             Shared Clipboard client context.
  * @param   fHeadless           Whether this is a headless connection or not.
  */
-int ShClBackendConnect(PSHCLCLIENT pClient, bool fHeadless);
+int ShClBackendConnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, bool fHeadless);
 
 /**
@@ -341,7 +377,8 @@
  *
  * @returns VBox status code.
- * @param   pClient             Shared Clipboard client context.
- */
-int ShClBackendDisconnect(PSHCLCLIENT pClient);
+ * @param   pBackend            Shared Clipboard backend to disconnect from.
+ * @param   pClient             Shared Clipboard client context.
+ */
+int ShClBackendDisconnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient);
 
 /**
@@ -349,9 +386,10 @@
  *
  * @returns VBox status code.
+ * @param   pBackend            Shared Clipboard backend to announce formats to.
  * @param   pClient             Shared Clipboard client context.
  * @param   fFormats            The announced formats from the guest,
  *                              VBOX_SHCL_FMT_XXX.
  */
-int ShClBackendFormatAnnounce(PSHCLCLIENT pClient, SHCLFORMATS fFormats);
+int ShClBackendFormatAnnounce(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, SHCLFORMATS fFormats);
 
 /**
@@ -359,4 +397,5 @@
  *
  * @returns VBox status code.
+ * @param   pBackend            Shared Clipboard backend to read data from.
  * @param   pClient             Shared Clipboard client context.
  * @param   pCmdCtx             Shared Clipboard command context.
@@ -369,5 +408,5 @@
  *          data
  */
-int ShClBackendReadData(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat,
+int ShClBackendReadData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat,
                         void *pvData, uint32_t cbData, uint32_t *pcbActual);
 
@@ -376,4 +415,5 @@
  *
  * @returns VBox status code.
+ * @param   pBackend            Shared Clipboard backend to write data to.
  * @param   pClient             Shared Clipboard client context.
  * @param   pCmdCtx             Shared Clipboard command context.
@@ -382,5 +422,5 @@
  * @param   cbData              Size (in bytes) of buffer clipboard data to write.
  */
-int ShClBackendWriteData(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat, void *pvData, uint32_t cbData);
+int ShClBackendWriteData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat, void *pvData, uint32_t cbData);
 
 /**
@@ -388,7 +428,8 @@
  *
  * @returns VBox status code.
- * @param   pClient             Shared Clipboard client context.
- */
-int ShClBackendSync(PSHCLCLIENT pClient);
+ * @param   pBackend            Shared Clipboard backend to synchronize.
+ * @param   pClient             Shared Clipboard client context.
+ */
+int ShClBackendSync(PSHCLBACKEND pBackend, PSHCLCLIENT pClient);
 /** @} */
 
@@ -401,24 +442,27 @@
  *
  * @returns VBox status code.
+ * @param   pBackend            Shared Clipboard backend to use.
  * @param   pClient             Shared Clipboard client context.
  * @param   pTransfer           Shared Clipboard transfer created.
  */
-int ShClBackendTransferCreate(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer);
+int ShClBackendTransferCreate(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer);
 /**
  * Called before a transfer gets destroyed.
  *
  * @returns VBox status code.
+ * @param   pBackend            Shared Clipboard backend to use.
  * @param   pClient             Shared Clipboard client context.
  * @param   pTransfer           Shared Clipboard transfer to destroy.
  */
-int ShClBackendTransferDestroy(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer);
+int ShClBackendTransferDestroy(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer);
 /**
  * Called when getting (determining) the transfer roots on the host side.
  *
  * @returns VBox status code.
+ * @param   pBackend            Shared Clipboard backend to use.
  * @param   pClient             Shared Clipboard client context.
  * @param   pTransfer           Shared Clipboard transfer to get roots for.
  */
-int ShClBackendTransferGetRoots(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer);
+int ShClBackendTransferGetRoots(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer);
 /** @} */
 #endif
Index: /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-transfers.cpp
===================================================================
--- /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-transfers.cpp	(revision 93494)
+++ /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-transfers.cpp	(revision 93495)
@@ -77,5 +77,5 @@
     PSHCLTRANSFER pTransfer;
     while ((pTransfer = ShClTransferCtxGetTransferByIndex(&pClient->Transfers.Ctx, uIdx++)))
-        ShClBackendTransferDestroy(pClient, pTransfer);
+        ShClBackendTransferDestroy(pClient->pBackend, pClient, pTransfer);
 
     ShClTransferCtxDestroy(&pClient->Transfers.Ctx);
@@ -1364,5 +1364,5 @@
             {
                 /* Get roots if this is a local write transfer (host -> guest). */
-                rc = ShClBackendTransferGetRoots(pClient, pTransfer);
+                rc = ShClBackendTransferGetRoots(pClient->pBackend, pClient, pTransfer);
             }
             else
@@ -1893,5 +1893,5 @@
                     if (RT_SUCCESS(rc))
                     {
-                        rc = ShClBackendTransferCreate(pClient, pTransfer);
+                        rc = ShClBackendTransferCreate(pClient->pBackend, pClient, pTransfer);
                         if (RT_SUCCESS(rc))
                         {
@@ -1954,5 +1954,5 @@
             if (RT_FAILURE(rc))
             {
-                ShClBackendTransferDestroy(pClient, pTransfer);
+                ShClBackendTransferDestroy(pClient->pBackend, pClient, pTransfer);
                 ShClTransferDestroy(pTransfer);
 
@@ -2009,5 +2009,5 @@
     if (RT_SUCCESS(rc2))
     {
-        ShClBackendTransferDestroy(pClient, pTransfer);
+        ShClBackendTransferDestroy(pClient->pBackend, pClient, pTransfer);
         ShClTransferDestroy(pTransfer);
         pTransfer = NULL;
Index: /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-win.cpp
===================================================================
--- /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-win.cpp	(revision 93494)
+++ /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-win.cpp	(revision 93495)
@@ -622,7 +622,7 @@
  */
 
-int ShClBackendInit(VBOXHGCMSVCFNTABLE *pTable)
-{
-    RT_NOREF(pTable);
+int ShClBackendInit(PSHCLBACKEND pBackend, VBOXHGCMSVCFNTABLE *pTable)
+{
+    RT_NOREF(pBackend, pTable);
 #ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
     HRESULT hr = OleInitialize(NULL);
@@ -639,6 +639,8 @@
 }
 
-void ShClBackendDestroy(void)
-{
+void ShClBackendDestroy(PSHCLBACKEND pBackend)
+{
+    RT_NOREF(pBackend);
+
 #ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
     OleSetClipboard(NULL); /* Make sure to flush the clipboard on destruction. */
@@ -647,7 +649,7 @@
 }
 
-int ShClBackendConnect(PSHCLCLIENT pClient, bool fHeadless)
-{
-    RT_NOREF(fHeadless);
+int ShClBackendConnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, bool fHeadless)
+{
+    RT_NOREF(pBackend, fHeadless);
 
     LogFlowFuncEnter();
@@ -680,12 +682,16 @@
 }
 
-int ShClBackendSync(PSHCLCLIENT pClient)
-{
+int ShClBackendSync(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
+{
+    RT_NOREF(pBackend);
+
     /* Sync the host clipboard content with the client. */
     return vboxClipboardSvcWinSyncInternal(pClient->State.pCtx);
 }
 
-int ShClBackendDisconnect(PSHCLCLIENT pClient)
-{
+int ShClBackendDisconnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
+{
+    RT_NOREF(pBackend);
+
     AssertPtrReturn(pClient, VERR_INVALID_POINTER);
 
@@ -727,6 +733,8 @@
 }
 
-int ShClBackendFormatAnnounce(PSHCLCLIENT pClient, SHCLFORMATS fFormats)
-{
+int ShClBackendFormatAnnounce(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, SHCLFORMATS fFormats)
+{
+    RT_NOREF(pBackend);
+
     AssertPtrReturn(pClient, VERR_INVALID_POINTER);
 
@@ -747,5 +755,5 @@
 }
 
-int ShClBackendReadData(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
+int ShClBackendReadData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
                         SHCLFORMAT uFmt, void *pvData, uint32_t cbData, uint32_t *pcbActual)
 {
@@ -755,5 +763,5 @@
     AssertPtrReturn(pcbActual, VERR_INVALID_POINTER);
 
-    RT_NOREF(pCmdCtx);
+    RT_NOREF(pBackend, pCmdCtx);
 
     AssertPtrReturn(pClient->State.pCtx, VERR_INVALID_POINTER);
@@ -861,8 +869,8 @@
 }
 
-int ShClBackendWriteData(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
+int ShClBackendWriteData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
                          SHCLFORMAT uFormat, void *pvData, uint32_t cbData)
 {
-    RT_NOREF(pClient, pCmdCtx, uFormat, pvData, cbData);
+    RT_NOREF(pBackend, pClient, pCmdCtx, uFormat, pvData, cbData);
 
     LogFlowFuncEnter();
@@ -875,7 +883,7 @@
 
 #ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
-int ShClBackendTransferCreate(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
-{
-    RT_NOREF(pClient, pTransfer);
+int ShClBackendTransferCreate(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
+{
+    RT_NOREF(pBackend, pClient, pTransfer);
 
     LogFlowFuncEnter();
@@ -884,6 +892,8 @@
 }
 
-int ShClBackendTransferDestroy(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
-{
+int ShClBackendTransferDestroy(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
+{
+    RT_NOREF(pBackend);
+
     LogFlowFuncEnter();
 
@@ -893,6 +903,8 @@
 }
 
-int ShClBackendTransferGetRoots(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
-{
+int ShClBackendTransferGetRoots(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
+{
+    RT_NOREF(pBackend);
+
     LogFlowFuncEnter();
 
Index: /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-x11-stubs.cpp
===================================================================
--- /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-x11-stubs.cpp	(revision 93494)
+++ /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-x11-stubs.cpp	(revision 93495)
@@ -41,5 +41,5 @@
  * Initialise the host side of the shared clipboard - called by the hgcm layer.
  */
-int ShClBackendInit(VBOXHGCMSVCFNTABLE *pTable)
+int ShClBackendInit(PSHCLBACKEND pBackend, VBOXHGCMSVCFNTABLE *pTable)
 {
     RT_NOREF(pTable);
@@ -51,12 +51,13 @@
  * Terminate the host side of the shared clipboard - called by the hgcm layer.
  */
-void ShClBackendDestroy(void)
+void ShClBackendDestroy(PSHCLBACKEND pBackend)
 {
+    RT_NOREF(pBackend);
     LogFlowFunc(("called, returning\n"));
 }
 
-int ShClBackendConnect(PSHCLCLIENT pClient, bool fHeadless)
+int ShClBackendConnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, bool fHeadless)
 {
-    RT_NOREF(pClient, fHeadless);
+    RT_NOREF(pBackend, pClient, fHeadless);
     LogFlowFunc(("called, returning VINF_SUCCESS\n"));
     return VINF_SUCCESS;
@@ -67,14 +68,14 @@
  * after a save and restore of the guest.
  */
-int ShClBackendSync(PSHCLCLIENT pClient)
+int ShClBackendSync(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
 {
-    RT_NOREF(pClient);
+    RT_NOREF(pBackend, pClient);
     LogFlowFunc(("called, returning VINF_SUCCESS\n"));
     return VINF_SUCCESS;
 }
 
-int ShClBackendDisconnect(PSHCLCLIENT pClient)
+int ShClBackendDisconnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
 {
-    RT_NOREF(pClient);
+    RT_NOREF(pBackend, pClient);
     return VINF_SUCCESS;
 }
@@ -84,7 +85,7 @@
  * Called by the HGCM clipboard subsystem.
  */
-int ShClBackendFormatAnnounce(PSHCLCLIENT pClient, SHCLFORMATS fFormats)
+int ShClBackendFormatAnnounce(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, SHCLFORMATS fFormats)
 {
-    RT_NOREF(pClient, fFormats);
+    RT_NOREF(pBackend, pClient, fFormats);
     return VINF_SUCCESS;
 }
@@ -93,8 +94,8 @@
  * Called by the HGCM clipboard subsystem when the guest wants to read the host clipboard.
  */
-int ShClBackendReadData(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
+int ShClBackendReadData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
                         SHCLFORMAT uFormat, void *pvData, uint32_t cbData, uint32_t *pcbActual)
 {
-    RT_NOREF(pClient, pCmdCtx, uFormat, pvData, cbData);
+    RT_NOREF(pBackend, pClient, pCmdCtx, uFormat, pvData, cbData);
 
     /* No data available. */
@@ -104,8 +105,8 @@
 }
 
-int ShClBackendWriteData(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
+int ShClBackendWriteData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
                          SHCLFORMAT uFormat, void *pvData, uint32_t cbData)
 {
-    RT_NOREF(pClient, pCmdCtx, uFormat, pvData, cbData);
+    RT_NOREF(pBackend, pClient, pCmdCtx, uFormat, pvData, cbData);
     return VERR_NOT_IMPLEMENTED;
 }
Index: /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-x11.cpp
===================================================================
--- /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-x11.cpp	(revision 93494)
+++ /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-x11.cpp	(revision 93495)
@@ -60,7 +60,16 @@
 };
 
-
-int ShClBackendInit(VBOXHGCMSVCFNTABLE *pTable)
-{
+/*********************************************************************************************************************************
+*   Prototypes                                                                                                                   *
+*********************************************************************************************************************************/
+static DECLCALLBACK(int) shClReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats, void *pvUser);
+static DECLCALLBACK(int) shClSendDataToDestCallback(PSHCLCONTEXT pCtx, void *pv, uint32_t cb, void *pvUser);
+static DECLCALLBACK(int) shClRequestDataFromSourceCallback(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser);
+
+
+int ShClBackendInit(PSHCLBACKEND pBackend, VBOXHGCMSVCFNTABLE *pTable)
+{
+    RT_NOREF(pBackend);
+
     LogFlowFuncEnter();
 
@@ -69,10 +78,33 @@
         pTable->acMaxClients[i] = RT_MIN(VBOX_SHARED_CLIPBOARD_X11_CONNECTIONS_MAX, pTable->acMaxClients[i]);
 
+    RT_ZERO(pBackend->Callbacks);
+    /* Use internal callbacks by default. */
+    pBackend->Callbacks.pfnReportFormats           = shClReportFormatsCallback;
+    pBackend->Callbacks.pfnOnRequestDataFromSource = shClRequestDataFromSourceCallback;
+    pBackend->Callbacks.pfnOnSendDataToDest        = shClSendDataToDestCallback;
+
     return VINF_SUCCESS;
 }
 
-void ShClBackendDestroy(void)
-{
+void ShClBackendDestroy(PSHCLBACKEND pBackend)
+{
+    RT_NOREF(pBackend);
+
     LogFlowFuncEnter();
+}
+
+void ShClBackendSetCallbacks(PSHCLBACKEND pBackend, PSHCLCALLBACKS pCallbacks)
+{
+#define SET_FN_IF_NOT_NULL(a_Fn) \
+    if (pCallbacks->pfn##a_Fn) \
+        pBackend->Callbacks.pfn##a_Fn = pCallbacks->pfn##a_Fn;
+
+    SET_FN_IF_NOT_NULL(ReportFormats);
+    SET_FN_IF_NOT_NULL(OnClipboardRead);
+    SET_FN_IF_NOT_NULL(OnClipboardWrite);
+    SET_FN_IF_NOT_NULL(OnRequestDataFromSource);
+    SET_FN_IF_NOT_NULL(OnSendDataToDest);
+
+#undef SET_FN_IF_NOT_NULL
 }
 
@@ -81,5 +113,5 @@
  *        the clipboard and leave ownership to X11.
  */
-int ShClBackendConnect(PSHCLCLIENT pClient, bool fHeadless)
+int ShClBackendConnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, bool fHeadless)
 {
     int rc;
@@ -99,5 +131,5 @@
         if (RT_SUCCESS(rc))
         {
-            rc = ShClX11Init(&pCtx->X11, pCtx, fHeadless);
+            rc = ShClX11Init(&pCtx->X11, &pBackend->Callbacks, pCtx, fHeadless);
             if (RT_SUCCESS(rc))
             {
@@ -133,6 +165,8 @@
 }
 
-int ShClBackendSync(PSHCLCLIENT pClient)
-{
+int ShClBackendSync(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
+{
+    RT_NOREF(pBackend);
+
     LogFlowFuncEnter();
 
@@ -149,6 +183,8 @@
  * Note!  Host glue code
  */
-int ShClBackendDisconnect(PSHCLCLIENT pClient)
-{
+int ShClBackendDisconnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
+{
+    RT_NOREF(pBackend);
+
     LogFlowFuncEnter();
 
@@ -178,6 +214,8 @@
 }
 
-int ShClBackendFormatAnnounce(PSHCLCLIENT pClient, SHCLFORMATS fFormats)
-{
+int ShClBackendFormatAnnounce(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, SHCLFORMATS fFormats)
+{
+    RT_NOREF(pBackend);
+
     int rc = ShClX11ReportFormatsToX11(&pClient->State.pCtx->X11, fFormats);
 
@@ -205,7 +243,9 @@
  *         the backend code.
  */
-int ShClBackendReadData(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat,
+int ShClBackendReadData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat,
                         void *pvData, uint32_t cbData, uint32_t *pcbActual)
 {
+    RT_NOREF(pBackend);
+
     AssertPtrReturn(pClient,   VERR_INVALID_POINTER);
     AssertPtrReturn(pCmdCtx,   VERR_INVALID_POINTER);
@@ -215,5 +255,5 @@
     RT_NOREF(pCmdCtx);
 
-    LogFlowFunc(("pClient=%p, uFormat=%02X, pv=%p, cb=%u, pcbActual=%p\n",
+    LogFlowFunc(("pClient=%p, uFormat=%#x, pv=%p, cb=%RU32, pcbActual=%p\n",
                  pClient, uFormat, pvData, cbData, pcbActual));
 
@@ -269,7 +309,7 @@
 }
 
-int ShClBackendWriteData(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat, void *pvData, uint32_t cbData)
-{
-    RT_NOREF(pClient, pCmdCtx, uFormat, pvData, cbData);
+int ShClBackendWriteData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat, void *pvData, uint32_t cbData)
+{
+    RT_NOREF(pBackend, pClient, pCmdCtx, uFormat, pvData, cbData);
 
     LogFlowFuncEnter();
@@ -281,6 +321,9 @@
 }
 
-DECLCALLBACK(void) ShClX11ReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats)
-{
+/** @copydoc SHCLBACKENDCALLBACKS::pfnReportFormats */
+static DECLCALLBACK(int) shClReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats, void *pvUser)
+{
+    RT_NOREF(pvUser);
+
     LogFlowFunc(("pCtx=%p, fFormats=%#x\n", pCtx, fFormats));
 
@@ -305,13 +348,18 @@
 
     LogFlowFuncLeaveRC(rc);
-}
-
-DECLCALLBACK(void) ShClX11ReportDataCallback(PSHCLCONTEXT pCtx, int rcCompletion,
-                                             CLIPREADCBREQ *pReq, void *pv, uint32_t cb)
-{
-    AssertPtrReturnVoid(pCtx);
-    AssertPtrReturnVoid(pReq);
-
-    LogFlowFunc(("rcCompletion=%Rrc, pReq=%p, pv=%p, cb=%RU32, idEvent=%RU32\n", rcCompletion, pReq, pv, cb, pReq->idEvent));
+    return rc;
+}
+
+/** @copydoc SHCLBACKENDCALLBACKS::pfnOnSendDataToDest */
+static DECLCALLBACK(int) shClSendDataToDestCallback(PSHCLCONTEXT pCtx, void *pv, uint32_t cb, void *pvUser)
+{
+    AssertPtrReturn(pCtx,   VERR_INVALID_POINTER);
+    AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+
+    PSHCLX11READDATAREQ pData = (PSHCLX11READDATAREQ)pvUser;
+    CLIPREADCBREQ      *pReq  = pData->pReq;
+
+    LogFlowFunc(("rcCompletion=%Rrc, pReq=%p, pv=%p, cb=%RU32, idEvent=%RU32\n",
+                 pData->rcCompletion, pReq, pv, cb, pReq->idEvent));
 
     if (pReq->idEvent != NIL_SHCLEVENTID)
@@ -320,5 +368,5 @@
 
         PSHCLEVENTPAYLOAD pPayload = NULL;
-        if (   RT_SUCCESS(rcCompletion)
+        if (   RT_SUCCESS(pData->rcCompletion)
             && pv
             && cb)
@@ -348,9 +396,14 @@
         RTMemFree(pReq);
 
-    LogRel2(("Shared Clipboard: Reading X11 clipboard data from host completed with %Rrc\n", rcCompletion));
-}
-
-DECLCALLBACK(int) ShClX11RequestDataCallback(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb)
-{
+    LogRel2(("Shared Clipboard: Reading X11 clipboard data from host completed with %Rrc\n", pData->rcCompletion));
+
+    return VINF_SUCCESS;
+}
+
+/** @copydoc SHCLBACKENDCALLBACKS::pfnOnRequestDataFromSource */
+static DECLCALLBACK(int) shClRequestDataFromSourceCallback(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
+{
+    RT_NOREF(pvUser);
+
     LogFlowFunc(("pCtx=%p, uFmt=0x%x\n", pCtx, uFmt));
 
@@ -437,6 +490,7 @@
 #ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
 
-int ShClBackendTransferCreate(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
-{
+int ShClBackendTransferCreate(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
+{
+    RT_NOREF(pBackend);
 #ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
     return ShClHttpTransferRegister(&pClient->State.pCtx->X11.HttpCtx, pTransfer);
@@ -447,6 +501,7 @@
 }
 
-int ShClBackendTransferDestroy(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
-{
+int ShClBackendTransferDestroy(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
+{
+    RT_NOREF(pBackend);
 #ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
     return ShClHttpTransferUnregister(&pClient->State.pCtx->X11.HttpCtx, pTransfer);
@@ -458,6 +513,8 @@
 }
 
-int ShClBackendTransferGetRoots(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
-{
+int ShClBackendTransferGetRoots(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
+{
+    RT_NOREF(pBackend);
+
     LogFlowFuncEnter();
 
Index: /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp
===================================================================
--- /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp	(revision 93494)
+++ /trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp	(revision 93495)
@@ -242,4 +242,7 @@
 *   Global Variables                                                                                                             *
 *********************************************************************************************************************************/
+/** The backend instance data.
+ *  Only one backend at a time is supported currently. */
+SHCLBACKEND         g_ShClBackend;
 PVBOXHGCMSVCHELPERS g_pHelpers;
 
@@ -282,4 +285,14 @@
 {
     return g_uMode;
+}
+
+/**
+ * Returns the Shared Clipboard backend in use.
+ *
+ * @returns Pointer to backend instance.
+ */
+PSHCLBACKEND ShClSvcGetBackend(void)
+{
+    return &g_ShClBackend;
 }
 
@@ -1563,5 +1576,5 @@
                     }
 #endif
-                    rc = ShClBackendFormatAnnounce(pClient, fFormats);
+                    rc = ShClBackendFormatAnnounce(&g_ShClBackend, pClient, fFormats);
                     if (RT_FAILURE(rc))
                         LogRel(("Shared Clipboard: Reporting guest clipboard formats to the host failed with %Rrc\n", rc));
@@ -1717,5 +1730,5 @@
     else
     {
-        rc = ShClBackendReadData(pClient, &cmdCtx, uFormat, pvData, cbData, &cbActual);
+        rc = ShClBackendReadData(&g_ShClBackend, pClient, &cmdCtx, uFormat, pvData, cbData, &cbActual);
         if (RT_SUCCESS(rc))
             LogRel2(("Shared Clipboard: Read host clipboard data (max %RU32 bytes), got %RU32 bytes\n", cbData, cbActual));
@@ -1892,5 +1905,5 @@
     {
         /* Let the backend implementation know. */
-        rc = ShClBackendWriteData(pClient, &cmdCtx, uFormat, pvData, cbData);
+        rc = ShClBackendWriteData(&g_ShClBackend, pClient, &cmdCtx, uFormat, pvData, cbData);
         if (RT_FAILURE(rc))
             LogRel(("Shared Clipboard: Writing guest clipboard data to the host failed with %Rrc\n", rc));
@@ -1970,5 +1983,5 @@
         shClSvcModeSet(VBOX_SHCL_MODE_OFF);
 
-        rc = ShClBackendInit(pTable);
+        rc = ShClBackendInit(ShClSvcGetBackend(), pTable);
 
         /* Clean up on failure, because 'svnUnload' will not be called
@@ -1988,5 +2001,5 @@
     LogFlowFuncEnter();
 
-    ShClBackendDestroy();
+    ShClBackendDestroy(ShClSvcGetBackend());
 
     RTCritSectDelete(&g_CritSect);
@@ -2016,5 +2029,5 @@
 #endif
 
-    ShClBackendDisconnect(pClient);
+    ShClBackendDisconnect(&g_ShClBackend, pClient);
 
     shClSvcClientDestroy(pClient);
@@ -2039,10 +2052,9 @@
          *        crash any worse that racing map insertion/removal. */
         g_mapClients[u32ClientID] = pClient; /** @todo Handle OOM / collisions? */
-
-        rc = ShClBackendConnect(pClient, ShClSvcGetHeadless());
+        rc = ShClBackendConnect(&g_ShClBackend, pClient, ShClSvcGetHeadless());
         if (RT_SUCCESS(rc))
         {
             /* Sync the host clipboard content with the client. */
-            rc = ShClBackendSync(pClient);
+            rc = ShClBackendSync(&g_ShClBackend, pClient);
             if (RT_SUCCESS(rc))
             {
@@ -2063,5 +2075,5 @@
 
             LogFunc(("ShClBackendSync failed: %Rrc\n", rc));
-            ShClBackendDisconnect(pClient);
+            ShClBackendDisconnect(&g_ShClBackend, pClient);
         }
         else
@@ -2654,5 +2666,5 @@
 
     /* Actual host data are to be reported to guest (SYNC). */
-    ShClBackendSync(pClient);
+    ShClBackendSync(&g_ShClBackend, pClient);
 
 #else  /* UNIT_TEST */
Index: /trunk/src/VBox/HostServices/SharedClipboard/testcase/Makefile.kmk
===================================================================
--- /trunk/src/VBox/HostServices/SharedClipboard/testcase/Makefile.kmk	(revision 93494)
+++ /trunk/src/VBox/HostServices/SharedClipboard/testcase/Makefile.kmk	(revision 93495)
@@ -20,4 +20,49 @@
 
 if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK)
+
+ if 0
+	#
+	# Testcase which mocks HGCM to also test the VbglR3-side of Shared Clipboard.
+	#
+	# Goal is to use and test as much guest side code as possible as a self-contained
+	# binary on the host here.
+	#
+	# Note: No #ifdef TESTCASE hacks or similar allowed, has to run
+	#       without #ifdef modifications to the core code!
+	#
+	PROGRAMS += tstClipboardMockHGCM
+	tstClipboardMockHGCM_TEMPLATE = VBOXR3TSTEXE
+	tstClipboardMockHGCM_DEFS     = VBOX_WITH_HGCM VBOX_WITH_SHARED_CLIPBOARD
+	tstClipboardMockHGCM_SOURCES  = \
+		../VBoxSharedClipboardSvc.cpp \
+		$(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-common.cpp \
+		$(PATH_ROOT)/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp \
+		$(PATH_ROOT)/src/VBox/HostServices/common/message.cpp \
+		tstClipboardMockHGCM.cpp
+	tstClipboardMockHGCM_LIBS     = $(LIB_RUNTIME)
+
+	if1of ($(KBUILD_TARGET), linux)
+	 tstClipboardMockHGCM_SOURCES.linux  += \
+		$(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-x11.cpp \
+		../VBoxSharedClipboardSvc-x11.cpp
+	 tstClipboardMockHGCM_LIBPATH = \
+		$(VBOX_LIBPATH_X11)
+	 tstClipboardMockHGCM_LIBS   += \
+		Xt \
+		X11
+	endif
+
+	tstClipboardMockHGCM_SOURCES.darwin += ../VBoxSharedClipboardSvc-darwin.cpp
+	tstClipboardMockHGCM_SOURCES.win    += ../VBoxSharedClipboardSvc-win.cpp
+
+	tstClipboardMockHGCM_CLEAN           = $(tstClipboardMockHGCM_0_OUTDIR)/tstClipboardMockHGCM.run
+
+	if defined(VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS)
+	 tstClipboardMockHGCM_DEFS    += VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+	 tstClipboardMockHGCM_SOURCES += \
+		../VBoxSharedClipboardSvc-transfers.cpp \
+	 $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-transfers.cpp
+	endif
+ endif
 
  #
Index: /trunk/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardMockHGCM.cpp
===================================================================
--- /trunk/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardMockHGCM.cpp	(revision 93495)
+++ /trunk/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardMockHGCM.cpp	(revision 93495)
@@ -0,0 +1,1488 @@
+/* $Id$ */
+/** @file
+ * Shared Clipboard host service test case.
+ */
+
+/*
+ * Copyright (C) 2011-2022 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include "../VBoxSharedClipboardSvc-internal.h"
+
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+#include <VBox/VBoxGuestLib.h>
+
+#ifdef RT_OS_LINUX
+# include <VBox/GuestHost/SharedClipboard-x11.h>
+#endif
+
+#include <iprt/assert.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/rand.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/test.h>
+#include <iprt/utf16.h>
+
+static RTTEST g_hTest;
+
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable);
+
+static SHCLCLIENT g_Client;
+
+typedef uint32_t HGCMCLIENTID;
+# define VBGLR3DECL(type) DECL_HIDDEN_NOTHROW(type) VBOXCALL
+
+RT_C_DECLS_BEGIN
+
+/** Simple call handle structure for the guest call completion callback. */
+struct VBOXHGCMCALLHANDLE_TYPEDEF
+{
+    /** Where to store the result code on call completion. */
+    int32_t rc;
+};
+
+typedef enum TSTHGCMMOCKFNTYPE
+{
+    TSTHGCMMOCKFNTYPE_NONE = 0,
+    TSTHGCMMOCKFNTYPE_CONNECT,
+    TSTHGCMMOCKFNTYPE_DISCONNECT,
+    TSTHGCMMOCKFNTYPE_CALL,
+    TSTHGCMMOCKFNTYPE_HOST_CALL
+} TSTHGCMMOCKFNTYPE;
+
+struct TSTHGCMMOCKSVC;
+
+typedef struct TSTHGCMMOCKCLIENT
+{
+    TSTHGCMMOCKSVC            *pSvc;
+    uint32_t                   idClient;
+    SHCLCLIENT                 Client;
+    VBOXHGCMCALLHANDLE_TYPEDEF hCall;
+    bool                       fAsyncExec;
+    RTSEMEVENT                 hEvent;
+} TSTHGCMMOCKCLIENT;
+/** Pointer to a mock HGCM client. */
+typedef TSTHGCMMOCKCLIENT *PTSTHGCMMOCKCLIENT;
+
+typedef struct TSTHGCMMOCKFN
+{
+    RTLISTNODE         Node;
+    TSTHGCMMOCKFNTYPE  enmType;
+    PTSTHGCMMOCKCLIENT pClient;
+    union
+    {
+        struct
+        {
+        } Connect;
+        struct
+        {
+        } Disconnect;
+        struct
+        {
+            int32_t             iFunc;
+            uint32_t            cParms;
+            PVBOXHGCMSVCPARM    pParms;
+            VBOXHGCMCALLHANDLE  hCall;
+        } Call;
+        struct
+        {
+            int32_t             iFunc;
+            uint32_t            cParms;
+            PVBOXHGCMSVCPARM    pParms;
+        } HostCall;
+    } u;
+} TSTHGCMMOCKFN;
+typedef TSTHGCMMOCKFN *PTSTHGCMMOCKFN;
+
+typedef struct TSTHGCMMOCKSVC
+{
+    VBOXHGCMSVCHELPERS fnHelpers;
+    HGCMCLIENTID       uNextClientId;
+    TSTHGCMMOCKCLIENT  aHgcmClient[4];
+    VBOXHGCMSVCFNTABLE fnTable;
+    RTTHREAD           hThread;
+    RTSEMEVENT         hEventQueue;
+    RTSEMEVENT         hEventWait;
+    /** Event semaphore for host calls. */
+    RTSEMEVENT         hEventHostCall;
+    RTLISTANCHOR       lstCall;
+    volatile bool      fShutdown;
+} TSTHGCMMOCKSVC;
+/** Pointer to a mock HGCM service. */
+typedef TSTHGCMMOCKSVC *PTSTHGCMMOCKSVC;
+
+static TSTHGCMMOCKSVC    s_tstHgcmSvc;
+
+struct TESTDESC;
+/** Pointer to a test description. */
+typedef TESTDESC *PTESTDESC;
+
+struct TESTPARMS;
+/** Pointer to a test parameter structure. */
+typedef TESTPARMS *PTESTPARMS;
+
+struct TESTCTX;
+/** Pointer to a test context. */
+typedef TESTCTX *PTESTCTX;
+
+/** Pointer a test descriptor. */
+typedef TESTDESC *PTESTDESC;
+
+typedef DECLCALLBACKTYPE(int, FNTESTSETUP,(PTESTPARMS pTstParms, void **ppvCtx));
+/** Pointer to an test setup callback. */
+typedef FNTESTSETUP *PFNTESTSETUP;
+
+typedef DECLCALLBACKTYPE(int, FNTESTEXEC,(PTESTPARMS pTstParms, void *pvCtx));
+/** Pointer to an test exec callback. */
+typedef FNTESTEXEC *PFNTESTEXEC;
+
+typedef DECLCALLBACKTYPE(int, FNTESTGSTTHREAD,(PTESTCTX pCtx, void *pvCtx));
+/** Pointer to an test guest thread callback. */
+typedef FNTESTGSTTHREAD *PFNTESTGSTTHREAD;
+
+typedef DECLCALLBACKTYPE(int, FNTESTDESTROY,(PTESTPARMS pTstParms, void *pvCtx));
+/** Pointer to an test destroy callback. */
+typedef FNTESTDESTROY *PFNTESTDESTROY;
+
+static int tstHgcmMockClientInit(PTSTHGCMMOCKCLIENT pClient, uint32_t idClient)
+{
+    RT_BZERO(pClient, sizeof(TSTHGCMMOCKCLIENT));
+
+    pClient->idClient = idClient;
+
+    return RTSemEventCreate(&pClient->hEvent);
+}
+
+static int tstHgcmMockClientDestroy(PTSTHGCMMOCKCLIENT pClient)
+{
+    int rc = RTSemEventDestroy(pClient->hEvent);
+    if (RT_SUCCESS(rc))
+    {
+        pClient->hEvent = NIL_RTSEMEVENT;
+    }
+
+    return rc;
+}
+
+#if 0
+static void tstBackendWriteData(HGCMCLIENTID idClient, SHCLFORMAT uFormat, void *pvData, size_t cbData)
+{
+    ShClBackendSetClipboardData(&s_tstHgcmClient[idClient].Client, uFormat, pvData, cbData);
+}
+
+/** Adds a host data read request message to the client's message queue. */
+static int tstSvcMockRequestDataFromGuest(uint32_t idClient, SHCLFORMATS fFormats, PSHCLEVENT *ppEvent)
+{
+    AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
+
+    int rc = ShClSvcGuestDataRequest(&s_tstHgcmClient[idClient].Client, fFormats, ppEvent);
+    RTTESTI_CHECK_RC_OK_RET(rc, rc);
+
+    return rc;
+}
+#endif
+
+static DECLCALLBACK(int) tstHgcmMockSvcConnect(PTSTHGCMMOCKSVC pSvc, void *pvService, uint32_t *pidClient)
+{
+    RT_NOREF(pvService);
+
+    PTSTHGCMMOCKFN pFn = (PTSTHGCMMOCKFN)RTMemAllocZ(sizeof(TSTHGCMMOCKFN));
+    AssertPtrReturn(pFn, VERR_NO_MEMORY);
+
+    PTSTHGCMMOCKCLIENT pClient = &pSvc->aHgcmClient[pSvc->uNextClientId];
+
+    int rc = tstHgcmMockClientInit(pClient, pSvc->uNextClientId);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    pFn->enmType = TSTHGCMMOCKFNTYPE_CONNECT;
+    pFn->pClient = pClient;
+
+    RTListAppend(&pSvc->lstCall, &pFn->Node);
+    pFn = NULL; /* Thread takes ownership now. */
+
+    int rc2 = RTSemEventSignal(pSvc->hEventQueue);
+    AssertRCReturn(rc2, rc2);
+
+    rc2 = RTSemEventWait(pClient->hEvent, RT_MS_30SEC);
+    AssertRCReturn(rc2, rc2);
+
+    ASMAtomicIncU32(&pSvc->uNextClientId);
+
+    *pidClient = pClient->idClient;
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) tstHgcmMockSvcDisconnect(PTSTHGCMMOCKSVC pSvc, void *pvService, uint32_t idClient)
+{
+    RT_NOREF(pvService);
+
+    PTSTHGCMMOCKCLIENT pClient = &pSvc->aHgcmClient[idClient];
+
+    PTSTHGCMMOCKFN pFn = (PTSTHGCMMOCKFN)RTMemAllocZ(sizeof(TSTHGCMMOCKFN));
+    AssertPtrReturn(pFn, VERR_NO_MEMORY);
+
+    pFn->enmType = TSTHGCMMOCKFNTYPE_DISCONNECT;
+    pFn->pClient = pClient;
+
+    RTListAppend(&pSvc->lstCall, &pFn->Node);
+    pFn = NULL; /* Thread takes ownership now. */
+
+    int rc2 = RTSemEventSignal(pSvc->hEventQueue);
+    AssertRCReturn(rc2, rc2);
+
+    rc2 = RTSemEventWait(pClient->hEvent, RT_MS_30SEC);
+    AssertRCReturn(rc2, rc2);
+
+    return tstHgcmMockClientDestroy(pClient);
+}
+
+static DECLCALLBACK(int) tstHgcmMockSvcCall(PTSTHGCMMOCKSVC pSvc, void *pvService, VBOXHGCMCALLHANDLE callHandle, uint32_t idClient, void *pvClient,
+                                            int32_t function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+    RT_NOREF(pvService, pvClient);
+
+    PTSTHGCMMOCKCLIENT pClient = &pSvc->aHgcmClient[idClient];
+
+    PTSTHGCMMOCKFN pFn = (PTSTHGCMMOCKFN)RTMemAllocZ(sizeof(TSTHGCMMOCKFN));
+    AssertPtrReturn(pFn, VERR_NO_MEMORY);
+
+    const size_t cbParms = cParms * sizeof(VBOXHGCMSVCPARM);
+
+    pFn->enmType         = TSTHGCMMOCKFNTYPE_CALL;
+    pFn->pClient         = pClient;
+
+    pFn->u.Call.hCall    = callHandle;
+    pFn->u.Call.iFunc    = function;
+    pFn->u.Call.pParms   = (PVBOXHGCMSVCPARM)RTMemDup(paParms, cbParms);
+    AssertPtrReturn(pFn->u.Call.pParms, VERR_NO_MEMORY);
+    pFn->u.Call.cParms   = cParms;
+
+    RTListAppend(&pSvc->lstCall, &pFn->Node);
+
+    int rc2 = RTSemEventSignal(pSvc->hEventQueue);
+    AssertRCReturn(rc2, rc2);
+
+    rc2 = RTSemEventWait(pSvc->aHgcmClient[idClient].hEvent, RT_INDEFINITE_WAIT);
+    AssertRCReturn(rc2, rc2);
+
+    memcpy(paParms, pFn->u.Call.pParms, cbParms);
+
+    return VINF_SUCCESS; /** @todo Return host call rc */
+}
+
+static DECLCALLBACK(int) tstHgcmMockSvcHostCall(PTSTHGCMMOCKSVC pSvc, void *pvService, int32_t function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+    RT_NOREF(pvService);
+
+    PTSTHGCMMOCKFN pFn = (PTSTHGCMMOCKFN)RTMemAllocZ(sizeof(TSTHGCMMOCKFN));
+    AssertPtrReturn(pFn, VERR_INVALID_POINTER);
+
+    pFn->enmType           = TSTHGCMMOCKFNTYPE_HOST_CALL;
+    pFn->u.HostCall.iFunc  = function;
+    pFn->u.HostCall.pParms = (PVBOXHGCMSVCPARM)RTMemDup(paParms, cParms * sizeof(VBOXHGCMSVCPARM));
+    AssertPtrReturn(pFn->u.HostCall.pParms, VERR_NO_MEMORY);
+    pFn->u.HostCall.cParms = cParms;
+
+    RTListAppend(&pSvc->lstCall, &pFn->Node);
+    pFn = NULL; /* Thread takes ownership now. */
+
+    int rc2 = RTSemEventSignal(pSvc->hEventQueue);
+    AssertRC(rc2);
+
+    rc2 = RTSemEventWait(pSvc->hEventHostCall, RT_INDEFINITE_WAIT);
+    AssertRCReturn(rc2, rc2);
+
+    return VINF_SUCCESS; /** @todo Return host call rc */
+}
+
+/** Call completion callback for guest calls. */
+static DECLCALLBACK(int) tstHgcmMockSvcCallComplete(VBOXHGCMCALLHANDLE callHandle, int32_t rc)
+{
+    PTSTHGCMMOCKSVC pSvc = &s_tstHgcmSvc;
+
+    for (size_t i = 0; RT_ELEMENTS(pSvc->aHgcmClient); i++)
+    {
+        PTSTHGCMMOCKCLIENT pClient = &pSvc->aHgcmClient[i];
+        if (&pClient->hCall == callHandle) /* Slow, but works for now. */
+        {
+            if (rc == VINF_HGCM_ASYNC_EXECUTE)
+            {
+                Assert(pClient->fAsyncExec == false);
+            }
+            else /* Complete call + notify client. */
+            {
+                callHandle->rc = rc;
+
+                int rc2 = RTSemEventSignal(pClient->hEvent);
+                AssertRCReturn(rc2, rc2);
+            }
+
+            return VINF_SUCCESS;
+        }
+    }
+
+    return VERR_NOT_FOUND;
+}
+
+static DECLCALLBACK(int) tstHgcmMockSvcThread(RTTHREAD hThread, void *pvUser)
+{
+    RT_NOREF(hThread);
+    PTSTHGCMMOCKSVC pSvc = (PTSTHGCMMOCKSVC)pvUser;
+
+    pSvc->uNextClientId  = 0;
+    pSvc->fnTable.cbSize     = sizeof(pSvc->fnTable);
+    pSvc->fnTable.u32Version = VBOX_HGCM_SVC_VERSION;
+
+    RT_ZERO(pSvc->fnHelpers);
+    pSvc->fnHelpers.pfnCallComplete = tstHgcmMockSvcCallComplete;
+    pSvc->fnTable.pHelpers          = &pSvc->fnHelpers;
+
+    int rc = VBoxHGCMSvcLoad(&pSvc->fnTable);
+    if (RT_SUCCESS(rc))
+    {
+        RTThreadUserSignal(hThread);
+
+        for (;;)
+        {
+            rc = RTSemEventWait(pSvc->hEventQueue, 10 /* ms */);
+            if (ASMAtomicReadBool(&pSvc->fShutdown))
+            {
+                rc = VINF_SUCCESS;
+                break;
+            }
+            if (rc == VERR_TIMEOUT)
+                continue;
+
+            PTSTHGCMMOCKFN pFn = RTListGetFirst(&pSvc->lstCall, TSTHGCMMOCKFN, Node);
+            if (pFn)
+            {
+                switch (pFn->enmType)
+                {
+                    case TSTHGCMMOCKFNTYPE_CONNECT:
+                    {
+                        rc = pSvc->fnTable.pfnConnect(pSvc->fnTable.pvService,
+                                                      pFn->pClient->idClient, &pFn->pClient->Client,
+                                                      VMMDEV_REQUESTOR_USR_NOT_GIVEN /* fRequestor */, false /* fRestoring */);
+
+                        int rc2 = RTSemEventSignal(pFn->pClient->hEvent);
+                        AssertRC(rc2);
+
+                        break;
+                    }
+
+                    case TSTHGCMMOCKFNTYPE_DISCONNECT:
+                    {
+                        rc = pSvc->fnTable.pfnDisconnect(pSvc->fnTable.pvService,
+                                                         pFn->pClient->idClient, &pFn->pClient->Client);
+                        break;
+                    }
+
+                    case TSTHGCMMOCKFNTYPE_CALL:
+                    {
+                        pSvc->fnTable.pfnCall(NULL, pFn->u.Call.hCall, pFn->pClient->idClient, &pFn->pClient->Client,
+                                              pFn->u.Call.iFunc, pFn->u.Call.cParms, pFn->u.Call.pParms, RTTimeMilliTS());
+
+                        /* Note: Call will be completed in the call completion callback. */
+                        break;
+                    }
+
+                    case TSTHGCMMOCKFNTYPE_HOST_CALL:
+                    {
+                        rc = pSvc->fnTable.pfnHostCall(NULL, pFn->u.HostCall.iFunc, pFn->u.HostCall.cParms, pFn->u.HostCall.pParms);
+
+                        int rc2 = RTSemEventSignal(pSvc->hEventHostCall);
+                        AssertRC(rc2);
+                        break;
+                    }
+
+                    default:
+                        AssertFailed();
+                        break;
+                }
+                RTListNodeRemove(&pFn->Node);
+                RTMemFree(pFn);
+            }
+        }
+    }
+
+    return rc;
+}
+
+static PTSTHGCMMOCKCLIENT tstHgcmMockSvcWaitForConnect(PTSTHGCMMOCKSVC pSvc)
+{
+    int rc = RTSemEventWait(pSvc->hEventWait, RT_MS_30SEC);
+    if (RT_SUCCESS(rc))
+    {
+        Assert(pSvc->uNextClientId);
+        return &pSvc->aHgcmClient[pSvc->uNextClientId - 1];
+    }
+    return NULL;
+}
+
+static int tstHgcmMockSvcCreate(PTSTHGCMMOCKSVC pSvc)
+{
+    RT_ZERO(pSvc->aHgcmClient);
+    pSvc->fShutdown = false;
+    int rc = RTSemEventCreate(&pSvc->hEventQueue);
+    if (RT_SUCCESS(rc))
+    {
+        rc = RTSemEventCreate(&pSvc->hEventHostCall);
+        if (RT_SUCCESS(rc))
+        {
+            rc = RTSemEventCreate(&pSvc->hEventWait);
+            if (RT_SUCCESS(rc))
+                RTListInit(&pSvc->lstCall);
+        }
+    }
+
+    return rc;
+}
+
+static int tstHgcmMockSvcDestroy(PTSTHGCMMOCKSVC pSvc)
+{
+    int rc = RTSemEventDestroy(pSvc->hEventQueue);
+    if (RT_SUCCESS(rc))
+    {
+        rc = RTSemEventDestroy(pSvc->hEventHostCall);
+        if (RT_SUCCESS(rc))
+            RTSemEventDestroy(pSvc->hEventWait);
+    }
+    return rc;
+}
+
+static int tstHgcmMockSvcStart(PTSTHGCMMOCKSVC pSvc)
+{
+    int rc = RTThreadCreate(&pSvc->hThread, tstHgcmMockSvcThread, pSvc, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
+                            "MockSvc");
+    if (RT_SUCCESS(rc))
+        rc = RTThreadUserWait(pSvc->hThread, RT_MS_30SEC);
+
+    return rc;
+}
+
+static int tstHgcmMockSvcStop(PTSTHGCMMOCKSVC pSvc)
+{
+    ASMAtomicWriteBool(&pSvc->fShutdown, true);
+
+    int rcThread;
+    int rc = RTThreadWait(pSvc->hThread, RT_MS_30SEC, &rcThread);
+    if (RT_SUCCESS(rc))
+        rc = rcThread;
+    if (RT_FAILURE(rc))
+        RTTestFailed(g_hTest, "Shutting down mock service failed with %Rrc\n", rc);
+
+    pSvc->hThread = NIL_RTTHREAD;
+
+    return rc;
+}
+
+VBGLR3DECL(int) VbglR3HGCMConnect(const char *pszServiceName, HGCMCLIENTID *pidClient)
+{
+    RT_NOREF(pszServiceName);
+
+    PTSTHGCMMOCKSVC pSvc = &s_tstHgcmSvc;
+
+    return tstHgcmMockSvcConnect(pSvc, pSvc->fnTable.pvService, pidClient);
+}
+
+VBGLR3DECL(int) VbglR3HGCMDisconnect(HGCMCLIENTID idClient)
+{
+    PTSTHGCMMOCKSVC pSvc = &s_tstHgcmSvc;
+
+    return tstHgcmMockSvcDisconnect(pSvc, pSvc->fnTable.pvService, idClient);
+}
+
+VBGLR3DECL(int) VbglR3HGCMCall(PVBGLIOCHGCMCALL pInfo, size_t cbInfo)
+{
+    AssertMsg(pInfo->Hdr.cbIn  == cbInfo, ("cbIn=%#x cbInfo=%#zx\n", pInfo->Hdr.cbIn, cbInfo));
+    AssertMsg(pInfo->Hdr.cbOut == cbInfo, ("cbOut=%#x cbInfo=%#zx\n", pInfo->Hdr.cbOut, cbInfo));
+    Assert(sizeof(*pInfo) + pInfo->cParms * sizeof(HGCMFunctionParameter) <= cbInfo);
+
+    HGCMFunctionParameter *offSrcParms = VBGL_HGCM_GET_CALL_PARMS(pInfo);
+    PVBOXHGCMSVCPARM       paDstParms  = (PVBOXHGCMSVCPARM)RTMemAlloc(pInfo->cParms * sizeof(VBOXHGCMSVCPARM));
+    for (uint16_t i = 0; i < pInfo->cParms; i++)
+    {
+        switch (offSrcParms->type)
+        {
+            case VMMDevHGCMParmType_32bit:
+            {
+                paDstParms[i].type     = VBOX_HGCM_SVC_PARM_32BIT;
+                paDstParms[i].u.uint32 = offSrcParms->u.value32;
+                break;
+            }
+
+            case VMMDevHGCMParmType_64bit:
+            {
+                paDstParms[i].type     = VBOX_HGCM_SVC_PARM_64BIT;
+                paDstParms[i].u.uint64 = offSrcParms->u.value64;
+                break;
+            }
+
+            case VMMDevHGCMParmType_LinAddr:
+            {
+                paDstParms[i].type           = VBOX_HGCM_SVC_PARM_PTR;
+                paDstParms[i].u.pointer.addr = (void *)offSrcParms->u.LinAddr.uAddr;
+                paDstParms[i].u.pointer.size = offSrcParms->u.LinAddr.cb;
+                break;
+            }
+
+            default:
+                AssertFailed();
+                break;
+        }
+
+        offSrcParms++;
+    }
+
+    PTSTHGCMMOCKSVC const pSvc = &s_tstHgcmSvc;
+
+    int rc2 = tstHgcmMockSvcCall(pSvc, pSvc->fnTable.pvService, &pSvc->aHgcmClient[pInfo->u32ClientID].hCall,
+                                 pInfo->u32ClientID, &pSvc->aHgcmClient[pInfo->u32ClientID].Client,
+                                 pInfo->u32Function, pInfo->cParms, paDstParms);
+    if (RT_SUCCESS(rc2))
+    {
+        offSrcParms = VBGL_HGCM_GET_CALL_PARMS(pInfo);
+
+        for (uint16_t i = 0; i < pInfo->cParms; i++)
+        {
+            paDstParms[i].type = offSrcParms->type;
+            switch (paDstParms[i].type)
+            {
+                case VMMDevHGCMParmType_32bit:
+                    offSrcParms->u.value32 = paDstParms[i].u.uint32;
+                    break;
+
+                case VMMDevHGCMParmType_64bit:
+                    offSrcParms->u.value64 = paDstParms[i].u.uint64;
+                    break;
+
+                case VMMDevHGCMParmType_LinAddr:
+                {
+                    offSrcParms->u.LinAddr.cb = paDstParms[i].u.pointer.size;
+                    break;
+                }
+
+                default:
+                    AssertFailed();
+                    break;
+            }
+
+            offSrcParms++;
+        }
+    }
+
+    RTMemFree(paDstParms);
+
+    if (RT_SUCCESS(rc2))
+        rc2 = pSvc->aHgcmClient[pInfo->u32ClientID].hCall.rc;
+
+    return rc2;
+}
+
+RT_C_DECLS_END
+
+
+/*********************************************************************************************************************************
+*   Shared Clipboard testing                                                                                                     *
+*********************************************************************************************************************************/
+
+typedef struct TESTTASK
+{
+    RTSEMEVENT  hEvent;
+    int         rcCompleted;
+    int         rcExpected;
+    SHCLFORMATS enmFmtHst;
+    SHCLFORMATS enmFmtGst;
+    /** For chunked reads / writes. */
+    size_t      cbChunk;
+    size_t      cbData;
+    void       *pvData;
+} TESTTASK;
+typedef TESTTASK *PTESTTASK;
+
+/**
+ * Structure for keeping a test context.
+ */
+typedef struct TESTCTX
+{
+    PTSTHGCMMOCKSVC      pSvc;
+    /** Currently we only support one task at a time. */
+    TESTTASK             Task;
+    struct
+    {
+        RTTHREAD         hThread;
+        VBGLR3SHCLCMDCTX CmdCtx;
+        volatile bool    fShutdown;
+        PFNTESTGSTTHREAD pfnThread;
+    } Guest;
+    struct
+    {
+        RTTHREAD         hThread;
+        volatile bool    fShutdown;
+    } Host;
+} TESTCTX;
+
+/** The one and only test context. */
+TESTCTX  g_TstCtx;
+
+/**
+ * Test parameters.
+ */
+typedef struct TESTPARMS
+{
+    /** Pointer to test context to use. */
+    PTESTCTX             pTstCtx;
+} TESTPARMS;
+
+typedef struct TESTDESC
+{
+    /** The setup callback. */
+    PFNTESTSETUP         pfnSetup;
+    /** The exec callback. */
+    PFNTESTEXEC          pfnExec;
+    /** The destruction callback. */
+    PFNTESTDESTROY       pfnDestroy;
+} TESTDESC;
+
+typedef struct SHCLCONTEXT
+{
+} SHCLCONTEXT;
+
+static int tstSetModeRc(PTSTHGCMMOCKSVC pSvc, uint32_t uMode, int rc)
+{
+    VBOXHGCMSVCPARM aParms[2];
+    HGCMSvcSetU32(&aParms[0], uMode);
+    int rc2 = tstHgcmMockSvcHostCall(pSvc, NULL, VBOX_SHCL_HOST_FN_SET_MODE, 1, aParms);
+    RTTESTI_CHECK_MSG_RET(rc == rc2, ("Expected %Rrc, got %Rrc\n", rc, rc2), rc2);
+    uint32_t const uModeRet = ShClSvcGetMode();
+    RTTESTI_CHECK_MSG_RET(uMode == uModeRet, ("Expected mode %RU32, got %RU32\n", uMode, uModeRet), VERR_WRONG_TYPE);
+    return rc2;
+}
+
+static int tstSetMode(PTSTHGCMMOCKSVC pSvc, uint32_t uMode)
+{
+    return tstSetModeRc(pSvc, uMode, VINF_SUCCESS);
+}
+
+static bool tstGetMode(PTSTHGCMMOCKSVC pSvc, uint32_t uModeExpected)
+{
+    RT_NOREF(pSvc);
+    RTTESTI_CHECK_RET(ShClSvcGetMode() == uModeExpected, false);
+    return true;
+}
+
+static void tstOperationModes(void)
+{
+    struct VBOXHGCMSVCPARM parms[2];
+    uint32_t u32Mode;
+    int rc;
+
+    RTTestISub("Testing VBOX_SHCL_HOST_FN_SET_MODE");
+
+    PTSTHGCMMOCKSVC pSvc = &s_tstHgcmSvc;
+
+    /* Reset global variable which doesn't reset itself. */
+    HGCMSvcSetU32(&parms[0], VBOX_SHCL_MODE_OFF);
+    rc = tstHgcmMockSvcHostCall(pSvc, NULL, VBOX_SHCL_HOST_FN_SET_MODE, 1, parms);
+    RTTESTI_CHECK_RC_OK(rc);
+    u32Mode = ShClSvcGetMode();
+    RTTESTI_CHECK_MSG(u32Mode == VBOX_SHCL_MODE_OFF, ("u32Mode=%u\n", (unsigned) u32Mode));
+
+    rc = tstHgcmMockSvcHostCall(pSvc, NULL, VBOX_SHCL_HOST_FN_SET_MODE, 0, parms);
+    RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
+
+    rc = tstHgcmMockSvcHostCall(pSvc, NULL, VBOX_SHCL_HOST_FN_SET_MODE, 2, parms);
+    RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
+
+    HGCMSvcSetU64(&parms[0], 99);
+    rc = tstHgcmMockSvcHostCall(pSvc, NULL, VBOX_SHCL_HOST_FN_SET_MODE, 1, parms);
+    RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
+
+    tstSetMode(pSvc, VBOX_SHCL_MODE_HOST_TO_GUEST);
+    tstSetModeRc(pSvc, 99, VERR_NOT_SUPPORTED);
+    tstGetMode(pSvc, VBOX_SHCL_MODE_OFF);
+}
+
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+static void testSetTransferMode(void)
+{
+    RTTestISub("Testing VBOX_SHCL_HOST_FN_SET_TRANSFER_MODE");
+
+    PTSTHGCMMOCKSVC pSvc = &s_tstHgcmSvc;
+
+    /* Invalid parameter. */
+    VBOXHGCMSVCPARM parms[2];
+    HGCMSvcSetU64(&parms[0], 99);
+    int rc = tstHgcmMockSvcHostCall(pSvc, NULL, VBOX_SHCL_HOST_FN_SET_TRANSFER_MODE, 1, parms);
+    RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
+
+    /* Invalid mode. */
+    HGCMSvcSetU32(&parms[0], 99);
+    rc = tstHgcmMockSvcHostCall(pSvc, NULL, VBOX_SHCL_HOST_FN_SET_TRANSFER_MODE, 1, parms);
+    RTTESTI_CHECK_RC(rc, VERR_INVALID_FLAGS);
+
+    /* Enable transfers. */
+    HGCMSvcSetU32(&parms[0], VBOX_SHCL_TRANSFER_MODE_ENABLED);
+    rc = tstHgcmMockSvcHostCall(pSvc, NULL, VBOX_SHCL_HOST_FN_SET_TRANSFER_MODE, 1, parms);
+    RTTESTI_CHECK_RC(rc, VINF_SUCCESS);
+
+    /* Disable transfers again. */
+    HGCMSvcSetU32(&parms[0], VBOX_SHCL_TRANSFER_MODE_DISABLED);
+    rc = s_tstHgcmSvc.fnTable.pfnHostCall(NULL, VBOX_SHCL_HOST_FN_SET_TRANSFER_MODE, 1, parms);
+    RTTESTI_CHECK_RC(rc, VINF_SUCCESS);
+}
+#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
+
+/* Does testing of VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, needed for providing compatibility to older Guest Additions clients. */
+static void testHostGetMsgOld(void)
+{
+    RTTestISub("Setting up VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT test");
+
+    VBOXHGCMSVCPARM parms[2];
+    RT_ZERO(parms);
+
+    /* Unless we are bidirectional the host message requests will be dropped. */
+    HGCMSvcSetU32(&parms[0], VBOX_SHCL_MODE_BIDIRECTIONAL);
+    int rc = s_tstHgcmSvc.fnTable.pfnHostCall(NULL, VBOX_SHCL_HOST_FN_SET_MODE, 1, parms);
+    RTTESTI_CHECK_RC_OK(rc);
+
+    RTTestISub("Testing one format, waiting guest u.Call.");
+    RT_ZERO(g_Client);
+    VBOXHGCMCALLHANDLE_TYPEDEF call;
+    rc = VERR_IPE_UNINITIALIZED_STATUS;
+    s_tstHgcmSvc.fnTable.pfnConnect(NULL, 1 /* clientId */, &g_Client, 0, 0);
+
+    HGCMSvcSetU32(&parms[0], 0);
+    HGCMSvcSetU32(&parms[1], 0);
+    s_tstHgcmSvc.fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
+    RTTESTI_CHECK_RC_OK(rc);
+
+    //testMsgAddReadData(&g_Client, VBOX_SHCL_FMT_UNICODETEXT);
+    RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHCL_HOST_MSG_READ_DATA);
+    RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHCL_FMT_UNICODETEXT);
+#if 0
+    RTTESTI_CHECK_RC_OK(u.Call.rc);
+    u.Call.rc = VERR_IPE_UNINITIALIZED_STATUS;
+    s_tstHgcmSrv.fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
+    RTTESTI_CHECK_RC(u.Call.rc, VINF_SUCCESS);
+    s_tstHgcmSrv.fnTable.pfnDisconnect(NULL, 1 /* clientId */, &g_Client);
+
+    RTTestISub("Testing one format, no waiting guest calls.");
+    RT_ZERO(g_Client);
+    s_tstHgcmSrv.fnTable.pfnConnect(NULL, 1 /* clientId */, &g_Client, 0, 0);
+    testMsgAddReadData(&g_Client, VBOX_SHCL_FMT_HTML);
+    HGCMSvcSetU32(&parms[0], 0);
+    HGCMSvcSetU32(&parms[1], 0);
+    u.Call.rc = VERR_IPE_UNINITIALIZED_STATUS;
+    s_tstHgcmSrv.fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
+    RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHCL_HOST_MSG_READ_DATA);
+    RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHCL_FMT_HTML);
+    RTTESTI_CHECK_RC_OK(u.Call.rc);
+    u.Call.rc = VERR_IPE_UNINITIALIZED_STATUS;
+    s_tstHgcmSrv.fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
+    RTTESTI_CHECK_RC(u.Call.rc, VINF_SUCCESS);
+    s_tstHgcmSrv.fnTable.pfnDisconnect(NULL, 1 /* clientId */, &g_Client);
+
+    RTTestISub("Testing two formats, waiting guest u.Call.");
+    RT_ZERO(g_Client);
+    s_tstHgcmSrv.fnTable.pfnConnect(NULL, 1 /* clientId */, &g_Client, 0, 0);
+    HGCMSvcSetU32(&parms[0], 0);
+    HGCMSvcSetU32(&parms[1], 0);
+    u.Call.rc = VERR_IPE_UNINITIALIZED_STATUS;
+    s_tstHgcmSrv.fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
+    RTTESTI_CHECK_RC(u.Call.rc, VERR_IPE_UNINITIALIZED_STATUS);  /* This should get updated only when the guest call completes. */
+    testMsgAddReadData(&g_Client, VBOX_SHCL_FMT_UNICODETEXT | VBOX_SHCL_FMT_HTML);
+    RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHCL_HOST_MSG_READ_DATA);
+    RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHCL_FMT_UNICODETEXT);
+    RTTESTI_CHECK_RC_OK(u.Call.rc);
+    u.Call.rc = VERR_IPE_UNINITIALIZED_STATUS;
+    s_tstHgcmSrv.fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
+    RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHCL_HOST_MSG_READ_DATA);
+    RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHCL_FMT_HTML);
+    RTTESTI_CHECK_RC_OK(u.Call.rc);
+    u.Call.rc = VERR_IPE_UNINITIALIZED_STATUS;
+    s_tstHgcmSrv.fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
+    RTTESTI_CHECK_RC(u.Call.rc, VERR_IPE_UNINITIALIZED_STATUS);  /* This call should not complete yet. */
+    s_tstHgcmSrv.fnTable.pfnDisconnect(NULL, 1 /* clientId */, &g_Client);
+
+    RTTestISub("Testing two formats, no waiting guest calls.");
+    RT_ZERO(g_Client);
+    s_tstHgcmSrv.fnTable.pfnConnect(NULL, 1 /* clientId */, &g_Client, 0, 0);
+    testMsgAddReadData(&g_Client, VBOX_SHCL_FMT_UNICODETEXT | VBOX_SHCL_FMT_HTML);
+    HGCMSvcSetU32(&parms[0], 0);
+    HGCMSvcSetU32(&parms[1], 0);
+    u.Call.rc = VERR_IPE_UNINITIALIZED_STATUS;
+    s_tstHgcmSrv.fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
+    RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHCL_HOST_MSG_READ_DATA);
+    RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHCL_FMT_UNICODETEXT);
+    RTTESTI_CHECK_RC_OK(u.Call.rc);
+    u.Call.rc = VERR_IPE_UNINITIALIZED_STATUS;
+    s_tstHgcmSrv.fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
+    RTTESTI_CHECK(parms[0].u.uint32 == VBOX_SHCL_HOST_MSG_READ_DATA);
+    RTTESTI_CHECK(parms[1].u.uint32 == VBOX_SHCL_FMT_HTML);
+    RTTESTI_CHECK_RC_OK(u.Call.rc);
+    u.Call.rc = VERR_IPE_UNINITIALIZED_STATUS;
+    s_tstHgcmSrv.fnTable.pfnCall(NULL, &call, 1 /* clientId */, &g_Client, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, 2, parms, 0);
+    RTTESTI_CHECK_RC(u.Call.rc, VERR_IPE_UNINITIALIZED_STATUS);  /* This call should not complete yet. */
+#endif
+    s_tstHgcmSvc.fnTable.pfnDisconnect(NULL, 1 /* clientId */, &g_Client);
+}
+
+static void testGuestSimple(void)
+{
+    RTTestISub("Testing client (guest) API - Simple");
+
+    PTSTHGCMMOCKSVC pSvc = &s_tstHgcmSvc;
+
+    /* Preparations. */
+    VBGLR3SHCLCMDCTX Ctx;
+    RT_ZERO(Ctx);
+
+    /*
+     * Multiple connects / disconnects.
+     */
+    RTTESTI_CHECK_RC_OK(VbglR3ClipboardConnectEx(&Ctx, VBOX_SHCL_GF_0_CONTEXT_ID));
+    RTTESTI_CHECK_RC_OK(VbglR3ClipboardDisconnectEx(&Ctx));
+    /* Report bogus guest features while connecting. */
+    RTTESTI_CHECK_RC_OK(VbglR3ClipboardConnectEx(&Ctx, 0xdeadbeef));
+    RTTESTI_CHECK_RC_OK(VbglR3ClipboardDisconnectEx(&Ctx));
+
+    RTTESTI_CHECK_RC_OK(VbglR3ClipboardConnectEx(&Ctx, VBOX_SHCL_GF_0_CONTEXT_ID));
+
+    /*
+     * Feature tests.
+     */
+
+    RTTESTI_CHECK_RC_OK(VbglR3ClipboardReportFeatures(Ctx.idClient, 0x0,        NULL /* pfHostFeatures */));
+    /* Report bogus features to the host. */
+    RTTESTI_CHECK_RC_OK(VbglR3ClipboardReportFeatures(Ctx.idClient, 0xdeadb33f, NULL /* pfHostFeatures */));
+
+    /*
+     * Access denied tests.
+     */
+
+    /* Try reading data from host. */
+    uint8_t abData[32]; uint32_t cbIgnored;
+    RTTESTI_CHECK_RC(VbglR3ClipboardReadData(Ctx.idClient, VBOX_SHCL_FMT_UNICODETEXT,
+                                             abData, sizeof(abData), &cbIgnored), VERR_ACCESS_DENIED);
+    /* Try writing data without reporting formats before (legacy). */
+    RTTESTI_CHECK_RC(VbglR3ClipboardWriteData(Ctx.idClient, 0xdeadb33f, abData, sizeof(abData)), VERR_ACCESS_DENIED);
+    /* Try writing data without reporting formats before. */
+    RTTESTI_CHECK_RC(VbglR3ClipboardWriteDataEx(&Ctx, 0xdeadb33f, abData, sizeof(abData)), VERR_ACCESS_DENIED);
+    /* Report bogus formats to the host. */
+    RTTESTI_CHECK_RC(VbglR3ClipboardReportFormats(Ctx.idClient, 0xdeadb33f), VERR_ACCESS_DENIED);
+    /* Report supported formats to host. */
+    RTTESTI_CHECK_RC(VbglR3ClipboardReportFormats(Ctx.idClient,
+                                                  VBOX_SHCL_FMT_UNICODETEXT | VBOX_SHCL_FMT_BITMAP | VBOX_SHCL_FMT_HTML),
+                                                  VERR_ACCESS_DENIED);
+    /*
+     * Access allowed tests.
+     */
+    tstSetMode(pSvc, VBOX_SHCL_MODE_BIDIRECTIONAL);
+
+    /* Try writing data without reporting formats before. */
+    RTTESTI_CHECK_RC_OK(VbglR3ClipboardWriteDataEx(&Ctx, 0xdeadb33f, abData, sizeof(abData)));
+    /* Try reading data from host. */
+    RTTESTI_CHECK_RC_OK(VbglR3ClipboardReadData(Ctx.idClient, VBOX_SHCL_FMT_UNICODETEXT,
+                                                abData, sizeof(abData), &cbIgnored));
+    /* Report bogus formats to the host. */
+    RTTESTI_CHECK_RC_OK(VbglR3ClipboardReportFormats(Ctx.idClient, 0xdeadb33f));
+    /* Report supported formats to host. */
+    RTTESTI_CHECK_RC_OK(VbglR3ClipboardReportFormats(Ctx.idClient,
+                                                     VBOX_SHCL_FMT_UNICODETEXT | VBOX_SHCL_FMT_BITMAP | VBOX_SHCL_FMT_HTML));
+    /* Tear down. */
+    RTTESTI_CHECK_RC_OK(VbglR3ClipboardDisconnectEx(&Ctx));
+}
+
+static void testGuestWrite(void)
+{
+    RTTestISub("Testing client (guest) API - Writing");
+}
+
+#if 0
+/**
+ * Generate a random codepoint for simple UTF-16 encoding.
+ */
+static RTUTF16 tstGetRandUtf16(void)
+{
+    RTUTF16 wc;
+    do
+    {
+        wc = (RTUTF16)RTRandU32Ex(1, 0xfffd);
+    } while (wc >= 0xd800 && wc <= 0xdfff);
+    return wc;
+}
+
+static PRTUTF16 tstGenerateUtf16StringA(uint32_t uCch)
+{
+    PRTUTF16 pwszRand = (PRTUTF16)RTMemAlloc((uCch + 1) * sizeof(RTUTF16));
+    for (uint32_t i = 0; i < uCch; i++)
+        pwszRand[i] = tstGetRandUtf16();
+    pwszRand[uCch] = 0;
+    return pwszRand;
+}
+#endif
+
+#if 0
+static void testGuestRead(void)
+{
+    RTTestISub("Testing client (guest) API - Reading");
+
+    /* Preparations. */
+    tstSetMode(VBOX_SHCL_MODE_BIDIRECTIONAL);
+
+    VBGLR3SHCLCMDCTX Ctx;
+    RTTESTI_CHECK_RC_OK(VbglR3ClipboardConnectEx(&Ctx, VBOX_SHCL_GF_0_CONTEXT_ID));
+    RTThreadSleep(500); /** @todo BUGBUG -- Seems to be a startup race when querying the initial clipboard formats. */
+
+    uint8_t abData[_4K]; uint32_t cbData; uint32_t cbRead;
+
+    /* Issue a host request that we want to read clipboard data from the guest. */
+    PSHCLEVENT pEvent;
+    tstSvcMockRequestDataFromGuest(Ctx.idClient, VBOX_SHCL_FMT_UNICODETEXT, &pEvent);
+
+    /* Write guest clipboard data to the host side. */
+    RTTESTI_CHECK_RC_OK(VbglR3ClipboardReportFormats(Ctx.idClient, VBOX_SHCL_FMT_UNICODETEXT));
+    cbData = RTRandU32Ex(1, sizeof(abData));
+    PRTUTF16 pwszStr = tstGenerateUtf16String(cbData);
+    RTTESTI_CHECK_RC_OK(VbglR3ClipboardWriteDataEx(&Ctx, VBOX_SHCL_FMT_UNICODETEXT, pwszStr, cbData));
+    RTMemFree(pwszStr);
+
+    PSHCLEVENTPAYLOAD pPayload;
+    int rc = ShClEventWait(pEvent, RT_MS_30SEC, &pPayload);
+    if (RT_SUCCESS(rc))
+    {
+
+    }
+    ShClEventRelease(pEvent);
+    pEvent = NULL;
+
+
+    /* Read clipboard data from the host back to the guest side. */
+    /* Note: Also could return VINF_BUFFER_OVERFLOW, so check for VINF_SUCCESS explicitly here. */
+    RTTESTI_CHECK_RC(VbglR3ClipboardReadDataEx(&Ctx, VBOX_SHCL_FMT_UNICODETEXT,
+                                               abData, sizeof(abData), &cbRead), VINF_SUCCESS);
+    RTTESTI_CHECK(cbRead == cbData);
+
+    RTPrintf("Data (%RU32): %ls\n", cbRead, (PCRTUTF16)abData);
+
+    /* Tear down. */
+    RTTESTI_CHECK_RC_OK(VbglR3ClipboardDisconnectEx(&Ctx));
+}
+#endif
+
+static DECLCALLBACK(int) tstGuestThread(RTTHREAD hThread, void *pvUser)
+{
+    RT_NOREF(hThread);
+    PTESTCTX pCtx = (PTESTCTX)pvUser;
+    AssertPtr(pCtx);
+
+    RTThreadUserSignal(hThread);
+
+    if (pCtx->Guest.pfnThread)
+        return pCtx->Guest.pfnThread(pCtx, NULL);
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) tstHostThread(RTTHREAD hThread, void *pvUser)
+{
+    RT_NOREF(hThread);
+    PTESTCTX pCtx = (PTESTCTX)pvUser;
+    AssertPtr(pCtx);
+
+    int rc = VINF_SUCCESS;
+
+    RTThreadUserSignal(hThread);
+
+    for (;;)
+    {
+        RTThreadSleep(100);
+
+        if (ASMAtomicReadBool(&pCtx->Host.fShutdown))
+            break;
+    }
+
+    return rc;
+}
+
+static void testSetHeadless(void)
+{
+    RTTestISub("Testing HOST_FN_SET_HEADLESS");
+
+    VBOXHGCMSVCPARM parms[2];
+    HGCMSvcSetU32(&parms[0], false);
+    int rc = s_tstHgcmSvc.fnTable.pfnHostCall(NULL, VBOX_SHCL_HOST_FN_SET_HEADLESS, 1, parms);
+    RTTESTI_CHECK_RC_OK(rc);
+    bool fHeadless = ShClSvcGetHeadless();
+    RTTESTI_CHECK_MSG(fHeadless == false, ("fHeadless=%RTbool\n", fHeadless));
+    rc = s_tstHgcmSvc.fnTable.pfnHostCall(NULL, VBOX_SHCL_HOST_FN_SET_HEADLESS, 0, parms);
+    RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
+    rc = s_tstHgcmSvc.fnTable.pfnHostCall(NULL, VBOX_SHCL_HOST_FN_SET_HEADLESS, 2, parms);
+    RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
+    HGCMSvcSetU64(&parms[0], 99);
+    rc = s_tstHgcmSvc.fnTable.pfnHostCall(NULL, VBOX_SHCL_HOST_FN_SET_HEADLESS, 1, parms);
+    RTTESTI_CHECK_RC(rc, VERR_INVALID_PARAMETER);
+    HGCMSvcSetU32(&parms[0], true);
+    rc = s_tstHgcmSvc.fnTable.pfnHostCall(NULL, VBOX_SHCL_HOST_FN_SET_HEADLESS, 1, parms);
+    RTTESTI_CHECK_RC_OK(rc);
+    fHeadless = ShClSvcGetHeadless();
+    RTTESTI_CHECK_MSG(fHeadless == true, ("fHeadless=%RTbool\n", fHeadless));
+    HGCMSvcSetU32(&parms[0], 99);
+    rc = s_tstHgcmSvc.fnTable.pfnHostCall(NULL, VBOX_SHCL_HOST_FN_SET_HEADLESS, 1, parms);
+    RTTESTI_CHECK_RC_OK(rc);
+    fHeadless = ShClSvcGetHeadless();
+    RTTESTI_CHECK_MSG(fHeadless == true, ("fHeadless=%RTbool\n", fHeadless));
+}
+
+static void testHostCall(void)
+{
+    tstOperationModes();
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+    testSetTransferMode();
+#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
+    testSetHeadless();
+}
+
+static int tstGuestStart(PTESTCTX pTstCtx, PFNTESTGSTTHREAD pFnThread)
+{
+    pTstCtx->Guest.pfnThread = pFnThread;
+
+    int rc = RTThreadCreate(&pTstCtx->Guest.hThread, tstGuestThread, pTstCtx, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
+                            "tstShClGst");
+    if (RT_SUCCESS(rc))
+        rc = RTThreadUserWait(pTstCtx->Guest.hThread, RT_MS_30SEC);
+
+    return rc;
+}
+
+static int tstGuestStop(PTESTCTX pTstCtx)
+{
+    ASMAtomicWriteBool(&pTstCtx->Guest.fShutdown, true);
+
+    int rcThread;
+    int rc = RTThreadWait(pTstCtx->Guest.hThread, RT_MS_30SEC, &rcThread);
+    if (RT_SUCCESS(rc))
+        rc = rcThread;
+    if (RT_FAILURE(rc))
+        RTTestFailed(g_hTest, "Shutting down guest thread failed with %Rrc\n", rc);
+
+    pTstCtx->Guest.hThread = NIL_RTTHREAD;
+
+    return rc;
+}
+
+static int tstHostStart(PTESTCTX pTstCtx)
+{
+    int rc = RTThreadCreate(&pTstCtx->Host.hThread, tstHostThread, pTstCtx, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
+                            "tstShClHst");
+    if (RT_SUCCESS(rc))
+        rc = RTThreadUserWait(pTstCtx->Host.hThread, RT_MS_30SEC);
+
+    return rc;
+}
+
+static int tstHostStop(PTESTCTX pTstCtx)
+{
+    ASMAtomicWriteBool(&pTstCtx->Host.fShutdown, true);
+
+    int rcThread;
+    int rc = RTThreadWait(pTstCtx->Host.hThread, RT_MS_30SEC, &rcThread);
+    if (RT_SUCCESS(rc))
+        rc = rcThread;
+    if (RT_FAILURE(rc))
+        RTTestFailed(g_hTest, "Shutting down host thread failed with %Rrc\n", rc);
+
+    pTstCtx->Host.hThread = NIL_RTTHREAD;
+
+    return rc;
+}
+
+#if defined(RT_OS_LINUX)
+static DECLCALLBACK(int) tstShClUserMockReportFormatsCallback(PSHCLCONTEXT pCtx, uint32_t fFormats, void *pvUser)
+{
+    RT_NOREF(pCtx, fFormats, pvUser);
+    RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "tstShClUserMockReportFormatsCallback: fFormats=%#x\n", fFormats);
+    return VINF_SUCCESS;
+}
+
+/*
+static DECLCALLBACK(int) tstTestReadFromHost_RequestDataFromSourceCallback(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
+{
+    RT_NOREF(pCtx, uFmt, ppv, pvUser);
+
+    PTESTTASK pTask = &TaskRead;
+
+    uint8_t *pvData = (uint8_t *)RTMemDup(pTask->pvData, pTask->cbData);
+
+    *ppv = pvData;
+    *pcb = pTask->cbData;
+
+    return VINF_SUCCESS;
+}
+*/
+
+#if 0
+static DECLCALLBACK(int) tstShClUserMockSendDataCallback(PSHCLCONTEXT pCtx, void *pv, uint32_t cb, void *pvUser)
+{
+    RT_NOREF(pCtx, pv, cb, pvUser);
+    RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "tstShClUserMockSendDataCallback\n");
+
+    PTESTTASK pTask = &TaskRead;
+
+    memcpy(pv, pTask->pvData, RT_MIN(pTask->cbData, cb));
+
+    return VINF_SUCCESS;
+}
+#endif
+
+static DECLCALLBACK(int) tstShClUserMockOnGetDataCallback(PSHCLCONTEXT pCtx, SHCLFORMAT uFmt, void **ppv, size_t *pcb, void *pvUser)
+{
+    RT_NOREF(pCtx, uFmt, pvUser);
+
+    PTESTTASK pTask = &g_TstCtx.Task;
+
+    uint8_t *pvData = pTask->cbData ? (uint8_t *)RTMemDup(pTask->pvData, pTask->cbData) : NULL;
+    size_t   cbData = pTask->cbData;
+
+    *ppv = pvData;
+    *pcb = cbData;
+
+    return VINF_SUCCESS;
+}
+#endif /* RT_OS_LINUX */
+
+typedef struct TSTUSERMOCK
+{
+#if defined(RT_OS_LINUX)
+    SHCLX11CTX   X11Ctx;
+#endif
+    PSHCLCONTEXT pCtx;
+} TSTUSERMOCK;
+typedef TSTUSERMOCK *PTSTUSERMOCK;
+
+static void tstShClUserMockInit(PTSTUSERMOCK pUsrMock, const char *pszName)
+{
+#if defined(RT_OS_LINUX)
+    SHCLCALLBACKS Callbacks;
+    RT_ZERO(Callbacks);
+    Callbacks.pfnReportFormats = tstShClUserMockReportFormatsCallback;
+    Callbacks.pfnOnClipboardRead     = tstShClUserMockOnGetDataCallback;
+
+    pUsrMock->pCtx = (PSHCLCONTEXT)RTMemAllocZ(sizeof(SHCLCONTEXT));
+    AssertPtrReturnVoid(pUsrMock->pCtx);
+
+    ShClX11Init(&pUsrMock->X11Ctx, &Callbacks, pUsrMock->pCtx, false);
+    ShClX11ThreadStartEx(&pUsrMock->X11Ctx, pszName, false /* fGrab */);
+    /* Give the clipboard time to synchronise. */
+    RTThreadSleep(500);
+#else
+    RT_NOREF(pUsrMock);
+#endif /* RT_OS_LINUX */
+}
+
+static void tstShClUserMockDestroy(PTSTUSERMOCK pUsrMock)
+{
+#if defined(RT_OS_LINUX)
+    ShClX11ThreadStop(&pUsrMock->X11Ctx);
+    ShClX11Destroy(&pUsrMock->X11Ctx);
+    RTMemFree(pUsrMock->pCtx);
+#else
+    RT_NOREF(pUsrMock);
+#endif
+}
+
+static int tstTaskGuestRead(PTESTCTX pCtx, PTESTTASK pTask)
+{
+    size_t   cbReadTotal = 0;
+    size_t   cbToRead    = pTask->cbData;
+
+    switch (pTask->enmFmtGst)
+    {
+        case VBOX_SHCL_FMT_UNICODETEXT:
+            cbToRead *= sizeof(RTUTF16);
+            break;
+
+        default:
+            break;
+    }
+
+    size_t   cbDst       = _64K;
+    uint8_t *pabDst      = (uint8_t *)RTMemAllocZ(cbDst);
+    AssertPtrReturn(pabDst, VERR_NO_MEMORY);
+
+    Assert(pTask->cbChunk);                  /* Buggy test? */
+    Assert(pTask->cbChunk <= pTask->cbData); /* Ditto. */
+
+    uint8_t *pabSrc = (uint8_t *)pTask->pvData;
+
+    do
+    {
+        /* Note! VbglR3ClipboardReadData() currently does not support chunked reads!
+          *      It in turn returns VINF_BUFFER_OVERFLOW when the supplied buffer was too small. */
+        uint32_t const cbChunk    = cbDst;
+        uint32_t const cbExpected = cbToRead;
+
+        uint32_t cbRead = 0;
+        RTTEST_CHECK_RC(g_hTest, VbglR3ClipboardReadData(pCtx->Guest.CmdCtx.idClient,
+                                                         pTask->enmFmtGst, pabDst, cbChunk, &cbRead), pTask->rcExpected);
+        RTTEST_CHECK_MSG(g_hTest, cbRead == cbExpected, (g_hTest, "Read %RU32 bytes, expected %RU32\n", cbRead, cbExpected));
+        RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Guest side received %RU32 bytes\n", cbRead);
+        cbReadTotal += cbRead;
+        Assert(cbReadTotal <= cbToRead);
+
+    } while (cbReadTotal < cbToRead);
+
+    if (pTask->enmFmtGst == VBOX_SHCL_FMT_UNICODETEXT)
+    {
+        RTTEST_CHECK_RC_OK(g_hTest, RTUtf16ValidateEncoding((PRTUTF16)pabDst));
+    }
+    else
+        RTTEST_CHECK(g_hTest, memcmp(pabSrc, pabDst, RT_MIN(pTask->cbData, cbDst) == 0));
+
+    RTMemFree(pabDst);
+
+    return VINF_SUCCESS;
+}
+
+static void tstTaskInit(PTESTTASK pTask)
+{
+    RTSemEventCreate(&pTask->hEvent);
+}
+
+static void tstTaskDestroy(PTESTTASK pTask)
+{
+    RTSemEventDestroy(pTask->hEvent);
+}
+
+static void tstTaskWait(PTESTTASK pTask, RTMSINTERVAL msTimeout)
+{
+    RTTEST_CHECK_RC_OK(g_hTest, RTSemEventWait(pTask->hEvent, msTimeout));
+    RTTEST_CHECK_RC(g_hTest, pTask->rcCompleted, pTask->rcExpected);
+}
+
+static void tstTaskSignal(PTESTTASK pTask, int rc)
+{
+    pTask->rcCompleted = rc;
+    RTTEST_CHECK_RC_OK(g_hTest, RTSemEventSignal(pTask->hEvent));
+}
+
+static DECLCALLBACK(int) tstTestReadFromHostThreadGuest(PTESTCTX pCtx, void *pvCtx)
+{
+    RT_NOREF(pvCtx);
+
+    RTThreadSleep(5000);
+
+    RT_ZERO(pCtx->Guest.CmdCtx);
+    RTTEST_CHECK_RC_OK(g_hTest, VbglR3ClipboardConnectEx(&pCtx->Guest.CmdCtx, VBOX_SHCL_GF_0_CONTEXT_ID));
+
+#if 1
+    PTESTTASK pTask = &pCtx->Task;
+    tstTaskGuestRead(pCtx, pTask);
+    tstTaskSignal(pTask, VINF_SUCCESS);
+#endif
+
+#if 0
+    for (;;)
+    {
+        PVBGLR3CLIPBOARDEVENT pEvent = (PVBGLR3CLIPBOARDEVENT)RTMemAllocZ(sizeof(VBGLR3CLIPBOARDEVENT));
+        AssertPtrBreakStmt(pEvent, rc = VERR_NO_MEMORY);
+
+        uint32_t idMsg  = 0;
+        uint32_t cParms = 0;
+        RTTEST_CHECK_RC_OK(g_hTest, VbglR3ClipboardMsgPeekWait(&pCtx->Guest.CmdCtx, &idMsg, &cParms, NULL /* pidRestoreCheck */));
+        RTTEST_CHECK_RC_OK(g_hTest, VbglR3ClipboardEventGetNext(idMsg, cParms, &pCtx->Guest.CmdCtx, pEvent));
+
+        if (pEvent)
+        {
+            VbglR3ClipboardEventFree(pEvent);
+            pEvent = NULL;
+        }
+
+        if (ASMAtomicReadBool(&pCtx->Guest.fShutdown))
+            break;
+
+        RTThreadSleep(100);
+    }
+#endif
+
+    RTTEST_CHECK_RC_OK(g_hTest, VbglR3ClipboardDisconnectEx(&pCtx->Guest.CmdCtx));
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) tstTestReadFromHostExec(PTESTPARMS pTstParms, void *pvCtx)
+{
+    RT_NOREF(pvCtx, pTstParms);
+
+    PTESTTASK pTask = &pTstParms->pTstCtx->Task;
+
+    pTask->enmFmtGst = VBOX_SHCL_FMT_UNICODETEXT;
+    pTask->enmFmtHst = pTask->enmFmtGst;
+    pTask->pvData    = RTStrAPrintf2("foo!");
+    pTask->cbData    = strlen((char *)pTask->pvData) + 1;
+    pTask->cbChunk   = pTask->cbData;
+
+    PTSTHGCMMOCKSVC    pSvc        = &s_tstHgcmSvc;
+    PTSTHGCMMOCKCLIENT pMockClient = tstHgcmMockSvcWaitForConnect(pSvc);
+    AssertPtrReturn(pMockClient, VERR_INVALID_POINTER);
+
+    bool fUseMock = false;
+    TSTUSERMOCK UsrMock;
+    if (fUseMock)
+        tstShClUserMockInit(&UsrMock, "tstX11Hst");
+
+    RTThreadSleep(RT_MS_1SEC * 4);
+
+#if 1
+    PSHCLBACKEND pBackend = ShClSvcGetBackend();
+
+    ShClBackendFormatAnnounce(pBackend, &pMockClient->Client, pTask->enmFmtHst);
+    tstTaskWait(pTask, RT_MS_30SEC);
+#endif
+
+RTThreadSleep(RT_MS_30SEC);
+
+    //PSHCLCLIENT pClient = &pMockClient->Client;
+
+#if 1
+    if (1)
+    {
+        //RTTEST_CHECK_RC_OK(g_hTest, ShClBackendMockSetData(pBackend, pTask->enmFmt, pwszStr, cbData));
+        //RTMemFree(pwszStr);
+    }
+#endif
+
+    if (fUseMock)
+        tstShClUserMockDestroy(&UsrMock);
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) tstTestReadFromHostSetup(PTESTPARMS pTstParms, void **ppvCtx)
+{
+    RT_NOREF(ppvCtx);
+
+    PTESTCTX pCtx = pTstParms->pTstCtx;
+
+    tstHostStart(pCtx);
+
+    PSHCLBACKEND pBackend = ShClSvcGetBackend();
+
+    SHCLCALLBACKS Callbacks;
+    RT_ZERO(Callbacks);
+    Callbacks.pfnReportFormats = tstShClUserMockReportFormatsCallback;
+    //Callbacks.pfnOnRequestDataFromSource = tstTestReadFromHost_RequestDataFromSourceCallback;
+    Callbacks.pfnOnClipboardRead     = tstShClUserMockOnGetDataCallback;
+    ShClBackendSetCallbacks(pBackend, &Callbacks);
+
+    tstGuestStart(pCtx, tstTestReadFromHostThreadGuest);
+
+    RTThreadSleep(1000);
+
+    tstSetMode(pCtx->pSvc, VBOX_SHCL_MODE_BIDIRECTIONAL);
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) tstTestReadFromHostDestroy(PTESTPARMS pTstParms, void *pvCtx)
+{
+    RT_NOREF(pvCtx);
+
+    int rc = VINF_SUCCESS;
+
+    tstGuestStop(pTstParms->pTstCtx);
+    tstHostStop(pTstParms->pTstCtx);
+
+    return rc;
+}
+
+/** Test definition table. */
+TESTDESC g_aTests[] =
+{
+    { tstTestReadFromHostSetup,       tstTestReadFromHostExec,      tstTestReadFromHostDestroy }
+};
+/** Number of tests defined. */
+unsigned g_cTests = RT_ELEMENTS(g_aTests);
+
+static int tstOne(PTSTHGCMMOCKSVC pSvc, PTESTDESC pTstDesc)
+{
+    PTESTCTX pTstCtx = &g_TstCtx;
+
+    TESTPARMS TstParms;
+    RT_ZERO(TstParms);
+
+    pTstCtx->pSvc    = pSvc;
+    TstParms.pTstCtx = pTstCtx;
+
+    void *pvCtx;
+    int rc = pTstDesc->pfnSetup(&TstParms, &pvCtx);
+    if (RT_SUCCESS(rc))
+    {
+        rc = pTstDesc->pfnExec(&TstParms, pvCtx);
+
+        int rc2 = pTstDesc->pfnDestroy(&TstParms, pvCtx);
+        if (RT_SUCCESS(rc))
+            rc = rc2;
+    }
+
+    return rc;
+}
+
+int main(int argc, char *argv[])
+{
+    /*
+     * Init the runtime, test and say hello.
+     */
+    const char *pcszExecName;
+    NOREF(argc);
+    pcszExecName = strrchr(argv[0], '/');
+    pcszExecName = pcszExecName ? pcszExecName + 1 : argv[0];
+    RTEXITCODE rcExit = RTTestInitAndCreate(pcszExecName, &g_hTest);
+    if (rcExit != RTEXITCODE_SUCCESS)
+        return rcExit;
+    RTTestBanner(g_hTest);
+
+    /* Don't let assertions in the host service panic (core dump) the test cases. */
+    RTAssertSetMayPanic(false);
+
+    /*
+     * Run the tests.
+     */
+    testGuestSimple();
+    testGuestWrite();
+    testHostCall();
+    testHostGetMsgOld();
+
+    PTSTHGCMMOCKSVC pSvc = &s_tstHgcmSvc;
+
+    tstHgcmMockSvcCreate(pSvc);
+    tstHgcmMockSvcStart(pSvc);
+
+    RT_ZERO(g_TstCtx);
+    tstTaskInit(&g_TstCtx.Task);
+    for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++)
+        tstOne(pSvc, &g_aTests[i]);
+    tstTaskDestroy(&g_TstCtx.Task);
+
+    tstHgcmMockSvcStop(pSvc);
+    tstHgcmMockSvcDestroy(pSvc);
+
+    /*
+     * Summary
+     */
+    return RTTestSummaryAndDestroy(g_hTest);
+}
+
Index: /trunk/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceHost.cpp
===================================================================
--- /trunk/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceHost.cpp	(revision 93494)
+++ /trunk/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceHost.cpp	(revision 93495)
@@ -310,17 +310,17 @@
 }
 
-int ShClBackendInit(VBOXHGCMSVCFNTABLE *) { return VINF_SUCCESS; }
-void ShClBackendDestroy() { }
-int ShClBackendDisconnect(PSHCLCLIENT) { return VINF_SUCCESS; }
-int ShClBackendConnect(PSHCLCLIENT, bool) { return VINF_SUCCESS; }
-int ShClBackendFormatAnnounce(PSHCLCLIENT, SHCLFORMATS) { AssertFailed(); return VINF_SUCCESS; }
-int ShClBackendReadData(PSHCLCLIENT, PSHCLCLIENTCMDCTX, SHCLFORMAT, void *, uint32_t, unsigned int *) { AssertFailed(); return VERR_WRONG_ORDER; }
-int ShClBackendWriteData(PSHCLCLIENT, PSHCLCLIENTCMDCTX, SHCLFORMAT, void *, uint32_t) { AssertFailed(); return VINF_SUCCESS; }
-int ShClBackendSync(PSHCLCLIENT) { return VINF_SUCCESS; }
+int ShClBackendInit(PSHCLBACKEND, VBOXHGCMSVCFNTABLE *) { return VINF_SUCCESS; }
+void ShClBackendDestroy(PSHCLBACKEND) { }
+int ShClBackendDisconnect(PSHCLBACKEND, PSHCLCLIENT) { return VINF_SUCCESS; }
+int ShClBackendConnect(PSHCLBACKEND, PSHCLCLIENT, bool) { return VINF_SUCCESS; }
+int ShClBackendFormatAnnounce(PSHCLBACKEND, PSHCLCLIENT, SHCLFORMATS) { AssertFailed(); return VINF_SUCCESS; }
+int ShClBackendReadData(PSHCLBACKEND, PSHCLCLIENT, PSHCLCLIENTCMDCTX, SHCLFORMAT, void *, uint32_t, unsigned int *) { AssertFailed(); return VERR_WRONG_ORDER; }
+int ShClBackendWriteData(PSHCLBACKEND, PSHCLCLIENT, PSHCLCLIENTCMDCTX, SHCLFORMAT, void *, uint32_t) { AssertFailed(); return VINF_SUCCESS; }
+int ShClBackendSync(PSHCLBACKEND, PSHCLCLIENT) { return VINF_SUCCESS; }
 
 #ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
-int ShClBackendTransferCreate(PSHCLCLIENT, PSHCLTRANSFER) { return VINF_SUCCESS; }
-int ShClBackendTransferDestroy(PSHCLCLIENT, PSHCLTRANSFER) { return VINF_SUCCESS; }
-int ShClBackendTransferGetRoots(PSHCLCLIENT, PSHCLTRANSFER) { return VINF_SUCCESS; }
+int ShClBackendTransferCreate(PSHCLBACKEND, PSHCLCLIENT, PSHCLTRANSFER) { return VINF_SUCCESS; }
+int ShClBackendTransferDestroy(PSHCLBACKEND, PSHCLCLIENT, PSHCLTRANSFER) { return VINF_SUCCESS; }
+int ShClBackendTransferGetRoots(PSHCLBACKEND, PSHCLCLIENT, PSHCLTRANSFER) { return VINF_SUCCESS; }
 #endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
 
Index: /trunk/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceImpl.cpp
===================================================================
--- /trunk/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceImpl.cpp	(revision 93494)
+++ /trunk/src/VBox/HostServices/SharedClipboard/testcase/tstClipboardServiceImpl.cpp	(revision 93495)
@@ -27,5 +27,5 @@
 #include <iprt/test.h>
 
-extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable);
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *ptable);
 
 static SHCLCLIENT g_Client;
@@ -55,12 +55,12 @@
 }
 
-int ShClBackendInit(VBOXHGCMSVCFNTABLE *) { return VINF_SUCCESS; }
-void ShClBackendDestroy(void) { }
-int ShClBackendDisconnect(PSHCLCLIENT) { return VINF_SUCCESS; }
-int ShClBackendConnect(PSHCLCLIENT, bool) { return VINF_SUCCESS; }
-int ShClBackendFormatAnnounce(PSHCLCLIENT, SHCLFORMATS) { AssertFailed(); return VINF_SUCCESS; }
-int ShClBackendReadData(PSHCLCLIENT, PSHCLCLIENTCMDCTX, SHCLFORMAT, void *, uint32_t, unsigned int *) { AssertFailed(); return VERR_WRONG_ORDER; }
-int ShClBackendWriteData(PSHCLCLIENT, PSHCLCLIENTCMDCTX, SHCLFORMAT, void *, uint32_t) { AssertFailed(); return VINF_SUCCESS; }
-int ShClBackendSync(PSHCLCLIENT) { return VINF_SUCCESS; }
+int ShClBackendInit(PSHCLBACKEND, VBOXHGCMSVCFNTABLE *) { return VINF_SUCCESS; }
+void ShClBackendDestroy(PSHCLBACKEND) { }
+int ShClBackendDisconnect(PSHCLBACKEND, PSHCLCLIENT) { return VINF_SUCCESS; }
+int ShClBackendConnect(PSHCLBACKEND, PSHCLCLIENT, bool) { return VINF_SUCCESS; }
+int ShClBackendFormatAnnounce(PSHCLBACKEND, PSHCLCLIENT, SHCLFORMATS) { AssertFailed(); return VINF_SUCCESS; }
+int ShClBackendReadData(PSHCLBACKEND, PSHCLCLIENT, PSHCLCLIENTCMDCTX, SHCLFORMAT, void *, uint32_t, unsigned int *) { AssertFailed(); return VERR_WRONG_ORDER; }
+int ShClBackendWriteData(PSHCLBACKEND, PSHCLCLIENT, PSHCLCLIENTCMDCTX, SHCLFORMAT, void *, uint32_t) { AssertFailed(); return VINF_SUCCESS; }
+int ShClBackendSync(PSHCLBACKEND, PSHCLCLIENT) { return VINF_SUCCESS; }
 
 static void testAnnounceAndReadData(void)
