summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-win.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-win.cpp890
1 files changed, 890 insertions, 0 deletions
diff --git a/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-win.cpp b/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-win.cpp
new file mode 100644
index 00000000..2a8db65d
--- /dev/null
+++ b/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-win.cpp
@@ -0,0 +1,890 @@
+/* $Id: VBoxSharedClipboardSvc-win.cpp $ */
+/** @file
+ * Shared Clipboard Service - Win32 host.
+ */
+
+/*
+ * Copyright (C) 2006-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
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
+#include <iprt/win/windows.h>
+
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+#include <VBox/GuestHost/clipboard-helper.h>
+#include <VBox/GuestHost/SharedClipboard-win.h>
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+# include <VBox/GuestHost/SharedClipboard-transfers.h>
+#endif
+
+#include <iprt/alloc.h>
+#include <iprt/string.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/ldr.h>
+#include <iprt/semaphore.h>
+#include <iprt/thread.h>
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+# include <iprt/utf16.h>
+#endif
+
+#include <process.h>
+#include <iprt/win/shlobj.h> /* Needed for shell objects. */
+
+#include "VBoxSharedClipboardSvc-internal.h"
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+# include "VBoxSharedClipboardSvc-transfers.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int vboxClipboardSvcWinSyncInternal(PSHCLCONTEXT pCtx);
+
+struct SHCLCONTEXT
+{
+ /** Handle for window message handling thread. */
+ RTTHREAD hThread;
+ /** Structure for keeping and communicating with service client. */
+ PSHCLCLIENT pClient;
+ /** Windows-specific context data. */
+ SHCLWINCTX Win;
+};
+
+
+/**
+ * Copy clipboard data into the guest buffer.
+ *
+ * At first attempt, guest will provide a buffer of default size.
+ * Usually 1K or 4K (see platform specific Guest Additions code around
+ * VbglR3ClipboardReadData calls). If this buffer is not big enough
+ * to fit host clipboard content, this function will return VINF_BUFFER_OVERFLOW
+ * and provide guest with host's clipboard buffer actual size. This will be a
+ * signal for the guest to re-read host clipboard data providing bigger buffer
+ * to store it.
+ *
+ * @returns IPRT status code.
+ * @returns VINF_BUFFER_OVERFLOW returned when guest buffer size if not big
+ * enough to store host clipboard data. This is a signal to the guest
+ * to re-issue host clipboard read request with bigger buffer size
+ * (specified in @a pcbActualDst output parameter).
+ * @param u32Format VBox clipboard format (VBOX_SHCL_FMT_XXX) of copied data.
+ * @param pvSrc Pointer to host clipboard data.
+ * @param cbSrc Size (in bytes) of actual clipboard data to copy.
+ * @param pvDst Pointer to guest buffer to store clipboard data.
+ * @param cbDst Size (in bytes) of guest buffer.
+ * @param pcbActualDst Actual size (in bytes) of host clipboard data.
+ * Only set if guest buffer size if not big enough
+ * to store host clipboard content. When set,
+ * function returns VINF_BUFFER_OVERFLOW.
+ */
+static int vboxClipboardSvcWinDataGet(uint32_t u32Format, const void *pvSrc, uint32_t cbSrc,
+ void *pvDst, uint32_t cbDst, uint32_t *pcbActualDst)
+{
+ AssertPtrReturn(pvSrc, VERR_INVALID_POINTER);
+ AssertReturn (cbSrc, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pvDst, VERR_INVALID_POINTER);
+ AssertReturn (cbDst, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcbActualDst, VERR_INVALID_POINTER);
+
+ LogFlowFunc(("cbSrc = %d, cbDst = %d\n", cbSrc, cbDst));
+
+ if ( u32Format == VBOX_SHCL_FMT_HTML
+ && SharedClipboardWinIsCFHTML((const char *)pvSrc))
+ {
+ /** @todo r=bird: Why the double conversion? */
+ char *pszBuf = NULL;
+ uint32_t cbBuf = 0;
+ int rc = SharedClipboardWinConvertCFHTMLToMIME((const char *)pvSrc, cbSrc, &pszBuf, &cbBuf);
+ if (RT_SUCCESS(rc))
+ {
+ *pcbActualDst = cbBuf;
+ if (cbBuf > cbDst)
+ {
+ /* Do not copy data. The dst buffer is not enough. */
+ RTMemFree(pszBuf);
+ return VINF_BUFFER_OVERFLOW;
+ }
+ memcpy(pvDst, pszBuf, cbBuf);
+ RTMemFree(pszBuf);
+ }
+ else
+ *pcbActualDst = 0;
+ }
+ else
+ {
+ *pcbActualDst = cbSrc; /* Tell the caller how much space we need. */
+
+ if (cbSrc > cbDst)
+ return VINF_BUFFER_OVERFLOW;
+
+ memcpy(pvDst, pvSrc, cbSrc);
+ }
+
+#ifdef LOG_ENABLED
+ ShClDbgDumpData(pvDst, cbSrc, u32Format);
+#endif
+
+ return VINF_SUCCESS;
+}
+
+static int vboxClipboardSvcWinDataRead(PSHCLCONTEXT pCtx, UINT uFormat, void **ppvData, uint32_t *pcbData)
+{
+ SHCLFORMAT fFormat = SharedClipboardWinClipboardFormatToVBox(uFormat);
+ LogFlowFunc(("uFormat=%u -> uFmt=0x%x\n", uFormat, fFormat));
+
+ if (fFormat == VBOX_SHCL_FMT_NONE)
+ {
+ LogRel2(("Shared Clipboard: Windows format %u not supported, ignoring\n", uFormat));
+ return VERR_NOT_SUPPORTED;
+ }
+
+ PSHCLEVENT pEvent;
+ int rc = ShClSvcGuestDataRequest(pCtx->pClient, fFormat, &pEvent);
+ if (RT_SUCCESS(rc))
+ {
+ PSHCLEVENTPAYLOAD pPayload;
+ rc = ShClEventWait(pEvent, 30 * 1000, &pPayload);
+ if (RT_SUCCESS(rc))
+ {
+ *ppvData = pPayload ? pPayload->pvData : NULL;
+ *pcbData = pPayload ? pPayload->cbData : 0;
+ }
+
+ ShClEventRelease(pEvent);
+ }
+
+ if (RT_FAILURE(rc))
+ LogRel(("Shared Clipboard: Reading guest clipboard data for Windows host failed with %Rrc\n", rc));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+static LRESULT CALLBACK vboxClipboardSvcWinWndProcMain(PSHCLCONTEXT pCtx,
+ HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) RT_NOTHROW_DEF
+{
+ AssertPtr(pCtx);
+
+ LRESULT lresultRc = 0;
+
+ const PSHCLWINCTX pWinCtx = &pCtx->Win;
+
+ switch (uMsg)
+ {
+ case WM_CLIPBOARDUPDATE:
+ {
+ LogFunc(("WM_CLIPBOARDUPDATE\n"));
+
+ int rc = RTCritSectEnter(&pWinCtx->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ const HWND hWndClipboardOwner = GetClipboardOwner();
+
+ LogFunc(("WM_CLIPBOARDUPDATE: hWndClipboardOwnerUs=%p, hWndNewClipboardOwner=%p\n",
+ pWinCtx->hWndClipboardOwnerUs, hWndClipboardOwner));
+
+ if (pWinCtx->hWndClipboardOwnerUs != hWndClipboardOwner)
+ {
+ int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
+ AssertRC(rc2);
+
+ /* Clipboard was updated by another application, retrieve formats and report back. */
+ rc = vboxClipboardSvcWinSyncInternal(pCtx);
+ }
+ else
+ {
+ int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
+ AssertRC(rc2);
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ LogRel(("Shared Clipboard: WM_CLIPBOARDUPDATE failed with %Rrc\n", rc));
+
+ break;
+ }
+
+ case WM_CHANGECBCHAIN:
+ {
+ LogFunc(("WM_CHANGECBCHAIN\n"));
+ lresultRc = SharedClipboardWinHandleWMChangeCBChain(pWinCtx, hWnd, uMsg, wParam, lParam);
+ break;
+ }
+
+ case WM_DRAWCLIPBOARD:
+ {
+ LogFunc(("WM_DRAWCLIPBOARD\n"));
+
+ int rc = RTCritSectEnter(&pWinCtx->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ const HWND hWndClipboardOwner = GetClipboardOwner();
+
+ LogFunc(("WM_DRAWCLIPBOARD: hWndClipboardOwnerUs=%p, hWndNewClipboardOwner=%p\n",
+ pWinCtx->hWndClipboardOwnerUs, hWndClipboardOwner));
+
+ if (pWinCtx->hWndClipboardOwnerUs != hWndClipboardOwner)
+ {
+ int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
+ AssertRC(rc2);
+
+ /* Clipboard was updated by another application, retrieve formats and report back. */
+ rc = vboxClipboardSvcWinSyncInternal(pCtx);
+ }
+ else
+ {
+ int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
+ AssertRC(rc2);
+ }
+ }
+
+ lresultRc = SharedClipboardWinChainPassToNext(pWinCtx, uMsg, wParam, lParam);
+ break;
+ }
+
+ case WM_TIMER:
+ {
+ int rc = SharedClipboardWinHandleWMTimer(pWinCtx);
+ AssertRC(rc);
+
+ break;
+ }
+
+ case WM_RENDERFORMAT:
+ {
+ LogFunc(("WM_RENDERFORMAT\n"));
+
+ /* Insert the requested clipboard format data into the clipboard. */
+ const UINT uFormat = (UINT)wParam;
+ const SHCLFORMAT fFormat = SharedClipboardWinClipboardFormatToVBox(uFormat);
+ LogFunc(("WM_RENDERFORMAT: uFormat=%u -> fFormat=0x%x\n", uFormat, fFormat));
+
+ if ( fFormat == VBOX_SHCL_FMT_NONE
+ || pCtx->pClient == NULL)
+ {
+ /* Unsupported clipboard format is requested. */
+ LogFunc(("WM_RENDERFORMAT unsupported format requested or client is not active\n"));
+ SharedClipboardWinClear();
+ }
+ else
+ {
+ void *pvData = NULL;
+ uint32_t cbData = 0;
+ int rc = vboxClipboardSvcWinDataRead(pCtx, uFormat, &pvData, &cbData);
+ if ( RT_SUCCESS(rc)
+ && pvData
+ && cbData)
+ {
+ /* Wrap HTML clipboard content info CF_HTML format if needed. */
+ if (fFormat == VBOX_SHCL_FMT_HTML
+ && !SharedClipboardWinIsCFHTML((char *)pvData))
+ {
+ char *pszWrapped = NULL;
+ uint32_t cbWrapped = 0;
+ rc = SharedClipboardWinConvertMIMEToCFHTML((char *)pvData, cbData, &pszWrapped, &cbWrapped);
+ if (RT_SUCCESS(rc))
+ {
+ /* Replace buffer with wrapped data content. */
+ RTMemFree(pvData);
+ pvData = (void *)pszWrapped;
+ cbData = cbWrapped;
+ }
+ else
+ LogRel(("Shared Clipboard: cannot convert HTML clipboard into CF_HTML format, rc=%Rrc\n", rc));
+ }
+
+ rc = SharedClipboardWinDataWrite(uFormat, pvData, cbData);
+ if (RT_FAILURE(rc))
+ LogRel(("Shared Clipboard: Setting clipboard data for Windows host failed with %Rrc\n", rc));
+
+ RTMemFree(pvData);
+ cbData = 0;
+ }
+
+ if (RT_FAILURE(rc))
+ SharedClipboardWinClear();
+ }
+
+ break;
+ }
+
+ case WM_RENDERALLFORMATS:
+ {
+ LogFunc(("WM_RENDERALLFORMATS\n"));
+
+ int rc = SharedClipboardWinHandleWMRenderAllFormats(pWinCtx, hWnd);
+ AssertRC(rc);
+
+ break;
+ }
+
+ case SHCL_WIN_WM_REPORT_FORMATS:
+ {
+ /* Announce available formats. Do not insert data -- will be inserted in WM_RENDERFORMAT (or via IDataObject). */
+ SHCLFORMATS fFormats = (uint32_t)lParam;
+ LogFunc(("SHCL_WIN_WM_REPORT_FORMATS: fFormats=%#xn", fFormats));
+
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+ if (fFormats & VBOX_SHCL_FMT_URI_LIST)
+ {
+ PSHCLTRANSFER pTransfer;
+ int rc = shClSvcTransferStart(pCtx->pClient,
+ SHCLTRANSFERDIR_FROM_REMOTE, SHCLSOURCE_REMOTE,
+ &pTransfer);
+ if (RT_SUCCESS(rc))
+ {
+ /* Create the IDataObject implementation the host OS needs and assign
+ * the newly created transfer to this object. */
+ rc = SharedClipboardWinTransferCreate(&pCtx->Win, pTransfer);
+
+ /* Note: The actual requesting + retrieving of data will be done in the IDataObject implementation
+ (ClipboardDataObjectImpl::GetData()). */
+ }
+ else
+ LogRel(("Shared Clipboard: Initializing read transfer failed with %Rrc\n", rc));
+ }
+ else
+ {
+#endif
+ int rc = SharedClipboardWinClearAndAnnounceFormats(pWinCtx, fFormats, hWnd);
+ if (RT_FAILURE(rc))
+ LogRel(("Shared Clipboard: Reporting clipboard formats %#x to Windows host failed with %Rrc\n", fFormats, rc));
+
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+ }
+#endif
+ LogFunc(("SHCL_WIN_WM_REPORT_FORMATS: lastErr=%ld\n", GetLastError()));
+ break;
+ }
+
+ case WM_DESTROY:
+ {
+ LogFunc(("WM_DESTROY\n"));
+
+ int rc = SharedClipboardWinHandleWMDestroy(pWinCtx);
+ AssertRC(rc);
+
+ PostQuitMessage(0);
+ break;
+ }
+
+ default:
+ lresultRc = DefWindowProc(hWnd, uMsg, wParam, lParam);
+ break;
+ }
+
+ LogFlowFunc(("LEAVE hWnd=%p, WM_ %u -> %#zx\n", hWnd, uMsg, lresultRc));
+ return lresultRc;
+}
+
+/**
+ * Static helper function for having a per-client proxy window instances.
+ */
+static LRESULT CALLBACK vboxClipboardSvcWinWndProcInstance(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) RT_NOTHROW_DEF
+{
+ LONG_PTR pUserData = GetWindowLongPtr(hWnd, GWLP_USERDATA);
+ AssertPtrReturn(pUserData, 0);
+
+ PSHCLCONTEXT pCtx = reinterpret_cast<PSHCLCONTEXT>(pUserData);
+ if (pCtx)
+ return vboxClipboardSvcWinWndProcMain(pCtx, hWnd, uMsg, wParam, lParam);
+
+ return 0;
+}
+
+/**
+ * Static helper function for routing Windows messages to a specific
+ * proxy window instance.
+ */
+static LRESULT CALLBACK vboxClipboardSvcWinWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) RT_NOTHROW_DEF
+{
+ /* Note: WM_NCCREATE is not the first ever message which arrives, but
+ * early enough for us. */
+ if (uMsg == WM_NCCREATE)
+ {
+ LogFlowFunc(("WM_NCCREATE\n"));
+
+ LPCREATESTRUCT pCS = (LPCREATESTRUCT)lParam;
+ AssertPtr(pCS);
+ SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pCS->lpCreateParams);
+ SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)vboxClipboardSvcWinWndProcInstance);
+
+ return vboxClipboardSvcWinWndProcInstance(hWnd, uMsg, wParam, lParam);
+ }
+
+ /* No window associated yet. */
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+DECLCALLBACK(int) vboxClipboardSvcWinThread(RTTHREAD hThreadSelf, void *pvUser)
+{
+ LogFlowFuncEnter();
+
+ bool fThreadSignalled = false;
+
+ const PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pvUser;
+ AssertPtr(pCtx);
+ const PSHCLWINCTX pWinCtx = &pCtx->Win;
+
+ HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
+
+ /* Register the Window Class. */
+ WNDCLASS wc;
+ RT_ZERO(wc);
+
+ wc.style = CS_NOCLOSE;
+ wc.lpfnWndProc = vboxClipboardSvcWinWndProc;
+ wc.hInstance = hInstance;
+ wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
+
+ /* Register an unique wnd class name. */
+ char szWndClassName[32];
+ RTStrPrintf2(szWndClassName, sizeof(szWndClassName),
+ "%s-%RU64", SHCL_WIN_WNDCLASS_NAME, RTThreadGetNative(hThreadSelf));
+ wc.lpszClassName = szWndClassName;
+
+ int rc;
+
+ ATOM atomWindowClass = RegisterClass(&wc);
+ if (atomWindowClass == 0)
+ {
+ LogFunc(("Failed to register window class\n"));
+ rc = VERR_NOT_SUPPORTED;
+ }
+ else
+ {
+ /* Create a window and make it a clipboard viewer. */
+ pWinCtx->hWnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
+ szWndClassName, szWndClassName,
+ WS_POPUPWINDOW,
+ -200, -200, 100, 100, NULL, NULL, hInstance, pCtx /* lpParam */);
+ if (pWinCtx->hWnd == NULL)
+ {
+ LogFunc(("Failed to create window\n"));
+ rc = VERR_NOT_SUPPORTED;
+ }
+ else
+ {
+ SetWindowPos(pWinCtx->hWnd, HWND_TOPMOST, -200, -200, 0, 0,
+ SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
+
+ rc = SharedClipboardWinChainAdd(&pCtx->Win);
+ if (RT_SUCCESS(rc))
+ {
+ if (!SharedClipboardWinIsNewAPI(&pWinCtx->newAPI))
+ pWinCtx->oldAPI.timerRefresh = SetTimer(pWinCtx->hWnd, 0, 10 * 1000, NULL);
+ }
+
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+ if (RT_SUCCESS(rc))
+ {
+ HRESULT hr = OleInitialize(NULL);
+ if (FAILED(hr))
+ {
+ LogRel(("Shared Clipboard: Initializing window thread OLE failed (%Rhrc) -- file transfers unavailable\n", hr));
+ /* Not critical, the rest of the clipboard might work. */
+ }
+ else
+ LogRel(("Shared Clipboard: Initialized window thread OLE\n"));
+ }
+#endif
+ int rc2 = RTThreadUserSignal(hThreadSelf);
+ AssertRC(rc2);
+
+ fThreadSignalled = true;
+
+ MSG msg;
+ BOOL msgret = 0;
+ while ((msgret = GetMessage(&msg, NULL, 0, 0)) > 0)
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ /*
+ * Window procedure can return error, * but this is exceptional situation that should be
+ * identified in testing.
+ */
+ Assert(msgret >= 0);
+ LogFunc(("Message loop finished. GetMessage returned %d, message id: %d \n", msgret, msg.message));
+
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+ OleSetClipboard(NULL); /* Make sure to flush the clipboard on destruction. */
+ OleUninitialize();
+#endif
+ }
+ }
+
+ pWinCtx->hWnd = NULL;
+
+ if (atomWindowClass != 0)
+ {
+ UnregisterClass(szWndClassName, hInstance);
+ atomWindowClass = 0;
+ }
+
+ if (!fThreadSignalled)
+ {
+ int rc2 = RTThreadUserSignal(hThreadSelf);
+ AssertRC(rc2);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Synchronizes the host and the guest clipboard formats by sending all supported host clipboard
+ * formats to the guest.
+ *
+ * @returns VBox status code, VINF_NO_CHANGE if no synchronization was required.
+ * @param pCtx Clipboard context to synchronize.
+ */
+static int vboxClipboardSvcWinSyncInternal(PSHCLCONTEXT pCtx)
+{
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ LogFlowFuncEnter();
+
+ int rc;
+
+ if (pCtx->pClient)
+ {
+ SHCLFORMATS fFormats = 0;
+ rc = SharedClipboardWinGetFormats(&pCtx->Win, &fFormats);
+ if ( RT_SUCCESS(rc)
+ && fFormats != VBOX_SHCL_FMT_NONE /** @todo r=bird: BUGBUG: revisit this. */
+ && ShClSvcIsBackendActive())
+ rc = ShClSvcHostReportFormats(pCtx->pClient, fFormats);
+ }
+ else /* If we don't have any client data (yet), bail out. */
+ rc = VINF_NO_CHANGE;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/*
+ * Public platform dependent functions.
+ */
+
+int ShClBackendInit(PSHCLBACKEND pBackend, VBOXHGCMSVCFNTABLE *pTable)
+{
+ RT_NOREF(pBackend, pTable);
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+ HRESULT hr = OleInitialize(NULL);
+ if (FAILED(hr))
+ {
+ LogRel(("Shared Clipboard: Initializing OLE failed (%Rhrc) -- file transfers unavailable\n", hr));
+ /* Not critical, the rest of the clipboard might work. */
+ }
+ else
+ LogRel(("Shared Clipboard: Initialized OLE\n"));
+#endif
+
+ return VINF_SUCCESS;
+}
+
+void ShClBackendDestroy(PSHCLBACKEND pBackend)
+{
+ RT_NOREF(pBackend);
+
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+ OleSetClipboard(NULL); /* Make sure to flush the clipboard on destruction. */
+ OleUninitialize();
+#endif
+}
+
+int ShClBackendConnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, bool fHeadless)
+{
+ RT_NOREF(pBackend, fHeadless);
+
+ LogFlowFuncEnter();
+
+ int rc;
+
+ PSHCLCONTEXT pCtx = (PSHCLCONTEXT)RTMemAllocZ(sizeof(SHCLCONTEXT));
+ if (pCtx)
+ {
+ rc = SharedClipboardWinCtxInit(&pCtx->Win);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTThreadCreate(&pCtx->hThread, vboxClipboardSvcWinThread, pCtx /* pvUser */, _64K /* Stack size */,
+ RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
+ if (RT_SUCCESS(rc))
+ {
+ int rc2 = RTThreadUserWait(pCtx->hThread, 30 * 1000 /* Timeout in ms */);
+ AssertRC(rc2);
+ }
+ }
+
+ pClient->State.pCtx = pCtx;
+ pClient->State.pCtx->pClient = pClient;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+int ShClBackendSync(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
+{
+ RT_NOREF(pBackend);
+
+ /* Sync the host clipboard content with the client. */
+ return vboxClipboardSvcWinSyncInternal(pClient->State.pCtx);
+}
+
+int ShClBackendDisconnect(PSHCLBACKEND pBackend, PSHCLCLIENT pClient)
+{
+ RT_NOREF(pBackend);
+
+ AssertPtrReturn(pClient, VERR_INVALID_POINTER);
+
+ LogFlowFuncEnter();
+
+ int rc = VINF_SUCCESS;
+
+ PSHCLCONTEXT pCtx = pClient->State.pCtx;
+ if (pCtx)
+ {
+ if (pCtx->Win.hWnd)
+ PostMessage(pCtx->Win.hWnd, WM_DESTROY, 0 /* wParam */, 0 /* lParam */);
+
+ if (pCtx->hThread != NIL_RTTHREAD)
+ {
+ LogFunc(("Waiting for thread to terminate ...\n"));
+
+ /* Wait for the window thread to terminate. */
+ rc = RTThreadWait(pCtx->hThread, 30 * 1000 /* Timeout in ms */, NULL);
+ if (RT_FAILURE(rc))
+ LogRel(("Shared Clipboard: Waiting for window thread termination failed with rc=%Rrc\n", rc));
+
+ pCtx->hThread = NIL_RTTHREAD;
+ }
+
+ SharedClipboardWinCtxDestroy(&pCtx->Win);
+
+ if (RT_SUCCESS(rc))
+ {
+ RTMemFree(pCtx);
+ pCtx = NULL;
+
+ pClient->State.pCtx = NULL;
+ }
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+int ShClBackendReportFormats(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, SHCLFORMATS fFormats)
+{
+ RT_NOREF(pBackend);
+
+ AssertPtrReturn(pClient, VERR_INVALID_POINTER);
+
+ PSHCLCONTEXT pCtx = pClient->State.pCtx;
+ AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+ LogFlowFunc(("fFormats=0x%x, hWnd=%p\n", fFormats, pCtx->Win.hWnd));
+
+ /*
+ * The guest announced formats. Forward to the window thread.
+ */
+ PostMessage(pCtx->Win.hWnd, SHCL_WIN_WM_REPORT_FORMATS,
+ 0 /* wParam */, fFormats /* lParam */);
+
+
+ LogFlowFuncLeaveRC(VINF_SUCCESS);
+ return VINF_SUCCESS;
+}
+
+int ShClBackendReadData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
+ SHCLFORMAT uFmt, void *pvData, uint32_t cbData, uint32_t *pcbActual)
+{
+ AssertPtrReturn(pClient, VERR_INVALID_POINTER);
+ AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcbActual, VERR_INVALID_POINTER);
+
+ RT_NOREF(pBackend, pCmdCtx);
+
+ AssertPtrReturn(pClient->State.pCtx, VERR_INVALID_POINTER);
+
+ LogFlowFunc(("uFmt=%#x\n", uFmt));
+
+ HANDLE hClip = NULL;
+
+ const PSHCLWINCTX pWinCtx = &pClient->State.pCtx->Win;
+
+ /*
+ * The guest wants to read data in the given format.
+ */
+ int rc = SharedClipboardWinOpen(pWinCtx->hWnd);
+ if (RT_SUCCESS(rc))
+ {
+ if (uFmt & VBOX_SHCL_FMT_BITMAP)
+ {
+ LogFunc(("CF_DIB\n"));
+ hClip = GetClipboardData(CF_DIB);
+ if (hClip != NULL)
+ {
+ LPVOID lp = GlobalLock(hClip);
+ if (lp != NULL)
+ {
+ rc = vboxClipboardSvcWinDataGet(VBOX_SHCL_FMT_BITMAP, lp, GlobalSize(hClip),
+ pvData, cbData, pcbActual);
+ GlobalUnlock(hClip);
+ }
+ else
+ {
+ hClip = NULL;
+ }
+ }
+ }
+ else if (uFmt & VBOX_SHCL_FMT_UNICODETEXT)
+ {
+ LogFunc(("CF_UNICODETEXT\n"));
+ hClip = GetClipboardData(CF_UNICODETEXT);
+ if (hClip != NULL)
+ {
+ LPWSTR uniString = (LPWSTR)GlobalLock(hClip);
+ if (uniString != NULL)
+ {
+ rc = vboxClipboardSvcWinDataGet(VBOX_SHCL_FMT_UNICODETEXT, uniString, (lstrlenW(uniString) + 1) * 2,
+ pvData, cbData, pcbActual);
+ GlobalUnlock(hClip);
+ }
+ else
+ {
+ hClip = NULL;
+ }
+ }
+ }
+ else if (uFmt & VBOX_SHCL_FMT_HTML)
+ {
+ LogFunc(("SHCL_WIN_REGFMT_HTML\n"));
+ UINT uRegFmt = RegisterClipboardFormat(SHCL_WIN_REGFMT_HTML);
+ if (uRegFmt != 0)
+ {
+ hClip = GetClipboardData(uRegFmt);
+ if (hClip != NULL)
+ {
+ LPVOID lp = GlobalLock(hClip);
+ if (lp != NULL)
+ {
+ rc = vboxClipboardSvcWinDataGet(VBOX_SHCL_FMT_HTML, lp, GlobalSize(hClip),
+ pvData, cbData, pcbActual);
+#ifdef LOG_ENABLED
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowFunc(("Raw HTML clipboard data from host:\n"));
+ ShClDbgDumpHtml((char *)pvData, cbData);
+ }
+#endif
+ GlobalUnlock(hClip);
+ }
+ else
+ {
+ hClip = NULL;
+ }
+ }
+ }
+ }
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+ else if (uFmt & VBOX_SHCL_FMT_URI_LIST)
+ {
+ AssertFailed(); /** @todo */
+ }
+#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
+ SharedClipboardWinClose();
+ }
+
+ if (hClip == NULL) /* Empty data is not fatal. */
+ {
+ /* Reply with empty data. */
+ vboxClipboardSvcWinDataGet(0, NULL, 0, pvData, cbData, pcbActual);
+ }
+
+ if (RT_FAILURE(rc))
+ LogRel(("Shared Clipboard: Error reading host clipboard data in format %#x from Windows, rc=%Rrc\n", uFmt, rc));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+int ShClBackendWriteData(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
+ SHCLFORMAT uFormat, void *pvData, uint32_t cbData)
+{
+ RT_NOREF(pBackend, pClient, pCmdCtx, uFormat, pvData, cbData);
+
+ LogFlowFuncEnter();
+
+ /* Nothing to do here yet. */
+
+ LogFlowFuncLeave();
+ return VINF_SUCCESS;
+}
+
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
+int ShClBackendTransferCreate(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
+{
+ RT_NOREF(pBackend, pClient, pTransfer);
+
+ LogFlowFuncEnter();
+
+ return VINF_SUCCESS;
+}
+
+int ShClBackendTransferDestroy(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
+{
+ RT_NOREF(pBackend);
+
+ LogFlowFuncEnter();
+
+ SharedClipboardWinTransferDestroy(&pClient->State.pCtx->Win, pTransfer);
+
+ return VINF_SUCCESS;
+}
+
+int ShClBackendTransferGetRoots(PSHCLBACKEND pBackend, PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
+{
+ RT_NOREF(pBackend);
+
+ LogFlowFuncEnter();
+
+ const PSHCLWINCTX pWinCtx = &pClient->State.pCtx->Win;
+
+ int rc = SharedClipboardWinGetRoots(pWinCtx, pTransfer);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
+