Index: trunk/src/VBox/HostServices/GuestControl/Makefile.kmk
===================================================================
--- trunk/src/VBox/HostServices/GuestControl/Makefile.kmk	(revision 98415)
+++ trunk/src/VBox/HostServices/GuestControl/Makefile.kmk	(revision 98526)
@@ -39,4 +39,7 @@
 VBoxGuestControlSvc_NAME.os2  = VBoxGCTL
 VBoxGuestControlSvc_DEFS      = VBOX_WITH_HGCM
+ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
+ VBoxGuestControlSvc_DEFS    += VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
+endif
 VBoxGuestControlSvc_INCS      = $(PATH_ROOT)/src/VBox/Main/include
 VBoxGuestControlSvc_INCS.win  = \
Index: trunk/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.cpp
===================================================================
--- trunk/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.cpp	(revision 98415)
+++ trunk/src/VBox/HostServices/GuestControl/VBoxGuestControlSvc.cpp	(revision 98526)
@@ -73,4 +73,6 @@
 #include <VBox/HostServices/GuestControlSvc.h>
 #include <VBox/GuestHost/GuestControl.h> /** @todo r=bird: Why two headers??? */
+
+#include "VBoxGuestControlSvc-internal.h"
 
 #include <VBox/err.h>
@@ -1626,4 +1628,5 @@
                         break;
                     case HOST_MSG_FILE_READ:
+                        RT_FALL_THROUGH();
                     case HOST_MSG_FILE_READ_AT:
                         HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_READ);  /* type */
@@ -1633,4 +1636,5 @@
                         break;
                     case HOST_MSG_FILE_WRITE:
+                        RT_FALL_THROUGH();
                     case HOST_MSG_FILE_WRITE_AT:
                         HGCMSvcSetU32(&aReplyParams[1], GUEST_FILE_NOTIFYTYPE_WRITE); /* type */
@@ -1657,12 +1661,34 @@
                         hostCallback(GUEST_MSG_FILE_NOTIFY, 4, aReplyParams);
                         break;
-
-                    case HOST_MSG_EXEC_GET_OUTPUT: /** @todo This can't be right/work. */
-                    case HOST_MSG_EXEC_TERMINATE:  /** @todo This can't be right/work. */
-                    case HOST_MSG_EXEC_WAIT_FOR:   /** @todo This can't be right/work. */
+#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
+                    case HOST_MSG_FS_QUERY_INFO:
+                        RT_FALL_THROUGH();
+                    case HOST_MSG_FS_CREATE_TEMP:
+                        RT_FALL_THROUGH();
+                    case HOST_MSG_FILE_REMOVE:
+                        RT_FALL_THROUGH();
+                    case HOST_MSG_DIR_OPEN:
+                        RT_FALL_THROUGH();
+                    case HOST_MSG_DIR_CLOSE:
+                        RT_FALL_THROUGH();
+                    case HOST_MSG_DIR_READ:
+                        RT_FALL_THROUGH();
+                    case HOST_MSG_DIR_REWIND:
+                        RT_FALL_THROUGH();
+                    case HOST_MSG_DIR_CREATE:
+                        RT_FALL_THROUGH();
+#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
+                    case HOST_MSG_EXEC_GET_OUTPUT: /** @todo BUGBUG This can't be right/work. */
+                    case HOST_MSG_EXEC_TERMINATE:  /** @todo BUGBUG This can't be right/work. */
+                    case HOST_MSG_EXEC_WAIT_FOR:   /** @todo BUGBUG This can't be right/work. */
+                        break;
+                    case HOST_MSG_DIR_REMOVE:
+                        RT_FALL_THROUGH();
+                    case HOST_MSG_PATH_RENAME:
+                        RT_FALL_THROUGH();
                     case HOST_MSG_PATH_USER_DOCUMENTS:
+                        RT_FALL_THROUGH();
                     case HOST_MSG_PATH_USER_HOME:
-                    case HOST_MSG_PATH_RENAME:
-                    case HOST_MSG_DIR_REMOVE:
+                        RT_FALL_THROUGH();
                     default:
                         HGCMSvcSetU32(&aReplyParams[1], pFirstMsg->mType);
@@ -2610,3 +2636,2 @@
     return rc;
 }
-
Index: trunk/src/VBox/HostServices/GuestControl/testcase/Makefile.kmk
===================================================================
--- trunk/src/VBox/HostServices/GuestControl/testcase/Makefile.kmk	(revision 98415)
+++ trunk/src/VBox/HostServices/GuestControl/testcase/Makefile.kmk	(revision 98526)
@@ -31,4 +31,36 @@
 if defined(VBOX_WITH_TESTCASES) && !defined(VBOX_ONLY_ADDITIONS) && !defined(VBOX_ONLY_SDK)
 
+ #
+ # Testcase which mocks HGCM to also test the VbglR3-side of Guest Control.
+ #
+ # 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 += tstGuestControlMockHGCM
+
+ tstGuestControlMockHGCM_TEMPLATE = VBoxR3TstExe
+ tstGuestControlMockHGCM_DEFS     = VBOX_WITH_HGCM VBOX_WITH_GUEST_CONTROL
+ tstGuestControlMockHGCM_SOURCES  = \
+ 	../VBoxGuestControlSvc.cpp \
+ 	$(PATH_ROOT)/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGuestCtrl.cpp \
+ 	$(PATH_ROOT)/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibMisc.cpp \
+ 	$(PATH_ROOT)/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3Lib.cpp \
+ 	$(PATH_ROOT)/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibGR.cpp \
+ 	$(PATH_ROOT)/src/VBox/HostServices/common/message.cpp \
+ 	tstGuestControlMockHGCM.cpp
+ tstGuestControlMockHGCM_LIBS     = $(LIB_RUNTIME)
+ tstGuestControlMockHGCM_CLEAN    = $(tstGuestControlMockHGCM_0_OUTDIR)/tstGuestControlMockHGCM.run
+
+ if 0 # Enable this if you want automatic runs after compilation.
+  $$(tstGuestControlMockHGCM_0_OUTDIR)/tstGuestControlMockHGCM.run: $$(tstGuestControlMockHGCM_1_STAGE_TARGET)
+	export VBOX_LOG_DEST=nofile; $(tstGuestControlMockHGCM_1_STAGE_TARGET) quiet
+	$(QUIET)$(APPEND) -t "$@" "done"
+  OTHERS += $(tstGuestControlMockHGCM_0_OUTDIR)/tstGuestControlMockHGCM.run
+ endif
+
+
  # Set this in LocalConfig.kmk if you are working on the guest property
  # service to automatically run the testcase at build time.
@@ -53,4 +85,13 @@
 endif
 
+#
+# List of above testcases that will be included in the ValKit.
+#
+ifdef VBOX_WITH_VALIDATIONKIT_UNITTESTS_PACKING
+ if1of ($(KBUILD_TARGET), linux solaris win)
+  VALKIT_UNITTESTS_WHITELIST_GUEST_ADDITIONS += \
+  	tstGuestControlMockHGCM
+ endif
+endif # VBOX_WITH_VALIDATIONKIT_UNITTESTS_PACKING
+
 include $(FILE_KBUILD_SUB_FOOTER)
-
Index: trunk/src/VBox/HostServices/GuestControl/testcase/tstGuestControlMockHGCM.cpp
===================================================================
--- trunk/src/VBox/HostServices/GuestControl/testcase/tstGuestControlMockHGCM.cpp	(revision 98526)
+++ trunk/src/VBox/HostServices/GuestControl/testcase/tstGuestControlMockHGCM.cpp	(revision 98526)
@@ -0,0 +1,322 @@
+/* $Id$ */
+/** @file
+ * Guest Control host service test case.
+ */
+
+/*
+ * Copyright (C) 2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include <VBox/HostServices/GuestControlSvc.h>
+#include <VBox/VBoxGuestLib.h>
+
+#include <VBox/GuestHost/HGCMMock.h>
+#include <VBox/GuestHost/HGCMMockUtils.h>
+
+#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 globals                                                                                                               *
+*********************************************************************************************************************************/
+static RTTEST     g_hTest;
+
+
+/*********************************************************************************************************************************
+*   Shared Clipboard testing                                                                                                     *
+*********************************************************************************************************************************/
+struct CLIPBOARDTESTDESC;
+/** Pointer to a test description. */
+typedef CLIPBOARDTESTDESC *PTESTDESC;
+
+struct CLIPBOARDTESTCTX;
+/** Pointer to a test context. */
+typedef CLIPBOARDTESTCTX *PCLIPBOARDTESTCTX;
+
+/** Pointer a test descriptor. */
+typedef CLIPBOARDTESTDESC *PTESTDESC;
+
+typedef DECLCALLBACKTYPE(int, FNTESTSETUP,(PCLIPBOARDTESTCTX pTstCtx, void **ppvCtx));
+/** Pointer to a test setup callback. */
+typedef FNTESTSETUP *PFNTESTSETUP;
+
+typedef DECLCALLBACKTYPE(int, FNTESTEXEC,(PCLIPBOARDTESTCTX pTstCtx, void *pvCtx));
+/** Pointer to a test exec callback. */
+typedef FNTESTEXEC *PFNTESTEXEC;
+
+typedef DECLCALLBACKTYPE(int, FNTESTDESTROY,(PCLIPBOARDTESTCTX pTstCtx, void *pvCtx));
+/** Pointer to a test destroy callback. */
+typedef FNTESTDESTROY *PFNTESTDESTROY;
+
+
+/**
+ * Structure for keeping a clipboard test task.
+ */
+typedef struct CLIPBOARDTESTTASK
+{
+
+} CLIPBOARDTESTTASK;
+typedef CLIPBOARDTESTTASK *PCLIPBOARDTESTTASK;
+
+/**
+ * Structure for keeping a clipboard test context.
+ */
+typedef struct CLIPBOARDTESTCTX
+{
+    /** The HGCM Mock utils context. */
+    TSTHGCMUTILSCTX   HGCM;
+    /** Clipboard-specific task data. */
+    CLIPBOARDTESTTASK Task;
+    struct
+    {
+        /** The VbglR3 Guest Control context to work on. */
+        VBGLR3GUESTCTRLCMDCTX CmdCtx;
+    } Guest;
+} CLIPBOARDTESTCTX;
+
+/** The one and only clipboard test context. One at a time. */
+CLIPBOARDTESTCTX g_TstCtx;
+
+/**
+ * Structure for keeping a clipboard test description.
+ */
+typedef struct CLIPBOARDTESTDESC
+{
+    /** The setup callback. */
+    PFNTESTSETUP         pfnSetup;
+    /** The exec callback. */
+    PFNTESTEXEC          pfnExec;
+    /** The destruction callback. */
+    PFNTESTDESTROY       pfnDestroy;
+} CLIPBOARDTESTDESC;
+
+typedef struct SHCLCONTEXT
+{
+} SHCLCONTEXT;
+
+
+static void testGuestSimple(void)
+{
+    RTTestISub("Testing client (guest) API - Simple");
+
+    PTSTHGCMMOCKSVC pSvc = TstHgcmMockSvcInst(); RT_NOREF(pSvc);
+
+    /* Preparations. */
+    VBGLR3GUESTCTRLCMDCTX Ctx;
+    RT_ZERO(Ctx);
+
+    /*
+     * Multiple connects / disconnects.
+     */
+    RTTESTI_CHECK_RC_OK(VbglR3GuestCtrlConnect(&Ctx.uClientID));
+    RTTESTI_CHECK_RC_OK(VbglR3GuestCtrlDisconnect(Ctx.uClientID));
+
+    /*
+     * Feature tests.
+     */
+    RTTESTI_CHECK_RC_OK(VbglR3GuestCtrlConnect(&Ctx.uClientID));
+    RTTESTI_CHECK_RC_OK(VbglR3GuestCtrlReportFeatures(Ctx.uClientID, 0x0,        NULL /* pfHostFeatures */));
+    /* Report bogus features to the host. */
+    RTTESTI_CHECK_RC_OK(VbglR3GuestCtrlReportFeatures(Ctx.uClientID, 0xdeadb33f, NULL /* pfHostFeatures */));
+    RTTESTI_CHECK_RC_OK(VbglR3GuestCtrlDisconnect(Ctx.uClientID));
+}
+
+static void testHostCall(void)
+{
+}
+
+
+/*********************************************************************************************************************************
+ * Test: Guest reading from host                                                                                                 *
+ ********************************************************************************************************************************/
+typedef struct TSTUSERMOCK
+{
+} TSTUSERMOCK;
+typedef TSTUSERMOCK *PTSTUSERMOCK;
+
+static void tstTestReadFromHost_MockInit(PTSTUSERMOCK pUsrMock, const char *pszName)
+{
+    RT_NOREF(pUsrMock, pszName);
+}
+
+static void tstTestReadFromHost_MockDestroy(PTSTUSERMOCK pUsrMock)
+{
+    RT_NOREF(pUsrMock);
+}
+
+static DECLCALLBACK(int) tstTestReadFromHost_ThreadGuest(PTSTHGCMUTILSCTX pCtx, void *pvCtx)
+{
+    RTThreadSleep(1000); /* Fudge; wait until the host has prepared the data for the clipboard. */
+
+    PCLIPBOARDTESTCTX  pTstCtx  = (PCLIPBOARDTESTCTX)pvCtx;
+    AssertPtr(pTstCtx);
+
+    RT_ZERO(pTstCtx->Guest.CmdCtx);
+    RTTEST_CHECK_RC_OK(g_hTest, VbglR3GuestCtrlConnect(&pTstCtx->Guest.CmdCtx.uClientID));
+
+    RTThreadSleep(1000); /* Fudge; wait until the host has prepared the data for the clipboard. */
+
+    /* Signal that the task ended. */
+    TstHGCMUtilsTaskSignal(&pCtx->Task, VINF_SUCCESS);
+
+    RTTEST_CHECK_RC_OK(g_hTest, VbglR3GuestCtrlDisconnect(pTstCtx->Guest.CmdCtx.uClientID));
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) tstTestReadFromHostSetup(PCLIPBOARDTESTCTX pTstCtx, void **ppvCtx)
+{
+    RT_NOREF(pTstCtx, ppvCtx);
+
+    int rc = VINF_SUCCESS;
+
+    return rc;
+}
+
+static DECLCALLBACK(int) tstTestReadFromHostExec(PCLIPBOARDTESTCTX pTstCtx, void *pvCtx)
+{
+    RT_NOREF(pvCtx);
+
+    TstHGCMUtilsGuestThreadStart(&pTstCtx->HGCM, tstTestReadFromHost_ThreadGuest, pTstCtx);
+
+    PTSTHGCMUTILSTASK pTask = (PTSTHGCMUTILSTASK)TstHGCMUtilsTaskGetCurrent(&pTstCtx->HGCM);
+
+    bool fUseMock = false;
+    TSTUSERMOCK UsrMock;
+    if (fUseMock)
+        tstTestReadFromHost_MockInit(&UsrMock, "tstX11Hst");
+
+    /* Wait until the task has been finished. */
+    TstHGCMUtilsTaskWait(pTask, RT_MS_30SEC);
+
+    if (fUseMock)
+        tstTestReadFromHost_MockDestroy(&UsrMock);
+
+    return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) tstTestReadFromHostDestroy(PCLIPBOARDTESTCTX pTstCtx, void *pvCtx)
+{
+    RT_NOREF(pvCtx);
+
+    int vrc = TstHGCMUtilsGuestThreadStop(&pTstCtx->HGCM);
+    AssertRC(vrc);
+    vrc = TstHGCMUtilsHostThreadStop(&pTstCtx->HGCM);
+    AssertRC(vrc);
+
+    return vrc;
+}
+
+
+/*********************************************************************************************************************************
+ * Main                                                                                                                          *
+ ********************************************************************************************************************************/
+
+/** Test definition table. */
+CLIPBOARDTESTDESC g_aTests[] =
+{
+    /* Tests guest reading clipboard data from the host.  */
+    { tstTestReadFromHostSetup,       tstTestReadFromHostExec,      tstTestReadFromHostDestroy }
+};
+/** Number of tests defined. */
+unsigned g_cTests = RT_ELEMENTS(g_aTests);
+
+static int tstOne(PTESTDESC pTstDesc)
+{
+    PCLIPBOARDTESTCTX pTstCtx = &g_TstCtx;
+
+    void *pvCtx;
+    int rc = pTstDesc->pfnSetup(pTstCtx, &pvCtx);
+    if (RT_SUCCESS(rc))
+    {
+        rc = pTstDesc->pfnExec(pTstCtx, pvCtx);
+
+        int rc2 = pTstDesc->pfnDestroy(pTstCtx, 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);
+
+#ifndef DEBUG_andy
+    /* Don't let assertions in the host service panic (core dump) the test cases. */
+    RTAssertSetMayPanic(false);
+#endif
+
+    PTSTHGCMMOCKSVC const pSvc = TstHgcmMockSvcInst();
+
+    TstHgcmMockSvcCreate(pSvc, 42 /** @todo */);
+    TstHgcmMockSvcStart(pSvc);
+
+    /*
+     * Run the tests.
+     */
+    if (1)
+    {
+        testGuestSimple();
+        testHostCall();
+    }
+
+    RT_ZERO(g_TstCtx);
+
+    PTSTHGCMUTILSCTX pCtx = &g_TstCtx.HGCM;
+    TstHGCMUtilsCtxInit(pCtx, pSvc);
+
+    PTSTHGCMUTILSTASK pTask = (PTSTHGCMUTILSTASK)TstHGCMUtilsTaskGetCurrent(pCtx);
+    TstHGCMUtilsTaskInit(pTask);
+    pTask->pvUser = &g_TstCtx.Task;
+
+    for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++)
+        tstOne(&g_aTests[i]);
+
+    TstHGCMUtilsTaskDestroy(pTask);
+
+    TstHgcmMockSvcStop(pSvc);
+    TstHgcmMockSvcDestroy(pSvc);
+
+    /*
+     * Summary
+     */
+    return RTTestSummaryAndDestroy(g_hTest);
+}
+
