VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxClipboard.cpp

Last change on this file was 103631, checked in by vboxsync, 2 months ago

Shared Clipboard: More cleanups (renaming Windows parts to match the other platforms). bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.3 KB
RevLine 
[55401]1/* $Id: VBoxClipboard.cpp 103631 2024-03-01 11:00:38Z vboxsync $ */
[9]2/** @file
[58144]3 * VBoxClipboard - Shared clipboard, Windows Guest Implementation.
[3198]4 */
5
6/*
[98103]7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[9]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
[9]26 */
27
[69361]28
[68630]29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
[78171]32#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
[79036]33#include <VBox/log.h>
34
[8850]35#include "VBoxTray.h"
[33966]36#include "VBoxHelpers.h"
[26742]37
[57741]38#include <iprt/asm.h>
[80444]39#include <iprt/errcore.h>
[95865]40#include <iprt/ldr.h>
[80444]41#include <iprt/mem.h>
[95865]42#include <iprt/utf16.h>
[57741]43
[78151]44#include <VBox/GuestHost/SharedClipboard.h>
[80444]45#include <VBox/GuestHost/SharedClipboard-win.h>
46#include <VBox/GuestHost/clipboard-helper.h>
[79497]47#include <VBox/HostServices/VBoxClipboardSvc.h> /* Temp, remove. */
[80849]48#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
[80862]49# include <VBox/GuestHost/SharedClipboard-transfers.h>
[78474]50#endif
[79036]51
[80849]52#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
[80324]53# include <iprt/win/shlobj.h>
54# include <iprt/win/shlwapi.h>
[78474]55#endif
56
57
[68630]58/*********************************************************************************************************************************
59* Structures and Typedefs *
60*********************************************************************************************************************************/
[82480]61struct SHCLCONTEXT
[9]62{
[78151]63 /** Pointer to the VBoxClient service environment. */
[80845]64 const VBOXSERVICEENV *pEnv;
[80283]65 /** Command context. */
[80845]66 VBGLR3SHCLCMDCTX CmdCtx;
[78151]67 /** Windows-specific context data. */
[80845]68 SHCLWINCTX Win;
[81025]69 /** Thread handle for window thread. */
70 RTTHREAD hThread;
71 /** Start indicator flag. */
72 bool fStarted;
73 /** Shutdown indicator flag. */
74 bool fShutdown;
[80849]75#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
[80845]76 /** Associated transfer data. */
[80858]77 SHCLTRANSFERCTX TransferCtx;
[78974]78#endif
[82480]79};
[9]80
[79630]81
[68630]82/*********************************************************************************************************************************
[78151]83* Static variables *
[68630]84*********************************************************************************************************************************/
[78151]85/** Static clipboard context (since it is the single instance). Directly used in the windows proc. */
[80662]86static SHCLCONTEXT g_Ctx = { NULL };
[78151]87/** Static window class name. */
[80664]88static char s_szClipWndClassName[] = SHCL_WIN_WNDCLASS_NAME;
[9]89
90
[79027]91/*********************************************************************************************************************************
92* Prototypes *
93*********************************************************************************************************************************/
[80849]94#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
[100433]95static DECLCALLBACK(void) vbtrShClTransferCreatedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx);
96static DECLCALLBACK(void) vbtrShClTransferDestroyCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx);
[100407]97static DECLCALLBACK(void) vbtrShClTransferInitializedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx);
98static DECLCALLBACK(void) vbtrShClTransferStartedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx);
99static DECLCALLBACK(void) vbtrShClTransferErrorCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx, int rc);
[79027]100#endif
101
102
[100394]103#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
[100204]104/**
[103631]105 * @copydoc ShClWinDataObject::CALLBACKS::pfnTransferBegin
[100394]106 *
[103631]107 * Called by ShClWinDataObject::GetData() when the user wants to paste data.
[100541]108 * This then requests a new transfer on the host.
109 *
[100394]110 * @thread Clipboard main thread.
111 */
[103631]112static DECLCALLBACK(int) vbtrShClDataObjectTransferBeginCallback(ShClWinDataObject::PCALLBACKCTX pCbCtx)
[100394]113{
114 LogFlowFuncEnter();
115
116 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
117 AssertPtr(pCtx);
118
119 int rc = VbglR3ClipboardTransferRequest(&pCtx->CmdCtx);
120
121 LogFlowFuncLeaveRC(rc);
122 return rc;
123}
124
125/**
[100433]126 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnCreated
127 *
128 * Called by ShClTransferCreate via VbglR3.
129 *
130 * @thread Clipboard main thread.
131 */
132static DECLCALLBACK(void) vbtrShClTransferCreatedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
133{
134 LogFlowFuncEnter();
135
136 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
137 AssertPtr(pCtx);
138
[103631]139 int rc = ShClWinTransferCreate(&pCtx->Win, pCbCtx->pTransfer);
[100433]140
141 LogFlowFuncLeaveRC(rc);
142}
143
144/**
145 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnDestroy
146 *
147 * Called by ShClTransferDestroy via VbglR3.
148 *
149 * @thread Clipboard main thread.
150 */
151static DECLCALLBACK(void) vbtrShClTransferDestroyCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
152{
153 LogFlowFuncEnter();
154
155 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
156 AssertPtr(pCtx);
157
[103631]158 ShClWinTransferDestroy(&pCtx->Win, pCbCtx->pTransfer);
[100433]159
160 LogFlowFuncLeave();
161}
162
163/**
[103442]164 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnInitialize
[100394]165 *
166 * Called by ShClTransferInit via VbglR3.
[103615]167 * For H->G: Called on transfer intialization to initialize the "in-flight" IDataObject for a data transfer.
168 * For G->H: Called on transfer intialization to populate the transfer's root list.
[100394]169 *
170 * @thread Clipboard main thread.
171 */
[103442]172static DECLCALLBACK(int) vbtrShClTransferInitializeCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
[100394]173{
174 LogFlowFuncEnter();
175
176 int rc = VINF_SUCCESS;
177
178 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
179 AssertPtr(pCtx);
180
[103450]181 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
182 AssertPtr(pTransfer);
183
184 switch(ShClTransferGetDir(pTransfer))
[100394]185 {
[103615]186 case SHCLTRANSFERDIR_FROM_REMOTE: /* H->G */
[100394]187 {
[103631]188 rc = ShClWinTransferInitialize(&pCtx->Win, pTransfer);
[100412]189 break;
190 }
191
[103615]192 case SHCLTRANSFERDIR_TO_REMOTE: /* G->H */
[100429]193 {
[103631]194 rc = ShClWinTransferGetRootsFromClipboard(&pCtx->Win, pTransfer);
[100429]195 break;
196 }
197
[100394]198 default:
199 break;
200 }
201
202 LogFlowFuncLeaveRC(rc);
[103442]203 return rc;
[100394]204}
205
206/**
[103615]207 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnInitialized
208 *
209 * Called by ShClTransferInit via VbglR3.
210 * For H->G: Called on transfer intialization to start the data transfer for the "in-flight" IDataObject.
211 * For G->H: Nothing to do here.
212 *
213 * @thread Clipboard main thread.
214 */
215static DECLCALLBACK(void) vbtrShClTransferInitializedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
216{
217 LogFlowFuncEnter();
218
219 int rc = VINF_SUCCESS;
220
221 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
222 AssertPtr(pCtx);
223
224 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
225 AssertPtr(pTransfer);
226
227 switch(ShClTransferGetDir(pTransfer))
228 {
229 case SHCLTRANSFERDIR_FROM_REMOTE: /* H->G */
230 {
[103631]231 rc = ShClWinTransferStart(&pCtx->Win, pTransfer);
[103615]232 break;
233 }
234
235 case SHCLTRANSFERDIR_TO_REMOTE: /* G->H */
236 break;
237
238 default:
239 break;
240 }
241
242 LogFlowFuncLeaveRC(rc);
243}
244
245/**
[100394]246 * Worker for a reading clipboard from the host.
247 *
[103365]248 * @returns VBox status code.
249 * @retval VERR_SHCLPB_NO_DATA if no clipboard data is available.
250 * @param pCtx Shared Clipbaord context to use.
251 * @param uFmt The format to read clipboard data in.
252 * @param ppvData Where to return the allocated data read.
253 * Must be free'd by the caller.
254 * @param pcbData Where to return number of bytes read.
255 * @param pvUser User-supplied context.
256 *
[100394]257 * @thread Clipboard main thread.
[103365]258 *
[100394]259 */
260static DECLCALLBACK(int) vbtrShClRequestDataFromSourceCallbackWorker(PSHCLCONTEXT pCtx,
[103365]261 SHCLFORMAT uFmt, void **ppvData, uint32_t *pcbData, void *pvUser)
[100394]262{
263 RT_NOREF(pvUser);
264
[103365]265 return VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, uFmt, ppvData, pcbData);
[100394]266}
267
268/**
[100204]269 * @copydoc SHCLCALLBACKS::pfnOnRequestDataFromSource
270 *
271 * Called from the IDataObject implementation to request data from the host.
272 *
273 * @thread shclwnd thread.
274 */
275DECLCALLBACK(int) vbtrShClRequestDataFromSourceCallback(PSHCLCONTEXT pCtx,
276 SHCLFORMAT uFmt, void **ppv, uint32_t *pcb, void *pvUser)
[79347]277{
[100204]278 PRTREQ pReq = NULL;
279 int rc = RTReqQueueCallEx(pCtx->Win.hReqQ, &pReq, SHCL_TIMEOUT_DEFAULT_MS, RTREQFLAGS_IPRT_STATUS,
280 (PFNRT)vbtrShClRequestDataFromSourceCallbackWorker, 5, pCtx, uFmt, ppv, pcb, pvUser);
281 RTReqRelease(pReq);
282 return rc;
283}
284
285/**
[100205]286 * @copydoc SHCLTRANSFERCALLBACKS::pfnOnStart
[100204]287 *
288 * Called from VbglR3 (main thread) to notify the IDataObject.
289 *
290 * @thread Clipboard main thread.
291 */
292static DECLCALLBACK(void) vbtrShClTransferStartedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
293{
[87611]294 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
[80905]295 AssertPtr(pCtx);
[79347]296
[87611]297 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
[80918]298 AssertPtr(pTransfer);
299
[100407]300 SHCLTRANSFERDIR const enmDir = ShClTransferGetDir(pTransfer);
[81025]301
[100407]302 int rc = VINF_SUCCESS;
[81025]303
[100204]304 /* The guest wants to transfer data to the host. */
[100407]305 if (enmDir == SHCLTRANSFERDIR_TO_REMOTE) /* G->H */
[80905]306 {
[103631]307 rc = ShClWinTransferGetRootsFromClipboard(&pCtx->Win, pTransfer);
[81212]308 }
[100407]309 else if (enmDir == SHCLTRANSFERDIR_FROM_REMOTE) /* H->G */
[81212]310 {
[100407]311 /* Nothing to do here. */
[80918]312 }
313 else
314 AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
[80905]315
[81212]316 if (RT_FAILURE(rc))
317 LogRel(("Shared Clipboard: Starting transfer failed, rc=%Rrc\n", rc));
[80905]318}
319
[100205]320/** @copydoc SHCLTRANSFERCALLBACKS::pfnOnCompleted */
[100204]321static DECLCALLBACK(void) vbtrShClTransferCompletedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx, int rcCompletion)
[80905]322{
[87611]323 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
[80905]324 AssertPtr(pCtx);
325
[100546]326 LogFlowFunc(("rcCompletion=%Rrc\n", rcCompletion));
327
[100541]328 LogRel2(("Shared Clipboard: Transfer %RU16 %s\n",
329 ShClTransferGetID(pCbCtx->pTransfer), rcCompletion == VERR_CANCELLED ? "canceled" : "complete"));
[100546]330
331 SHCLTRANSFERSTATUS enmSts;
332
333 switch (rcCompletion)
334 {
335 case VERR_CANCELLED:
336 enmSts = SHCLTRANSFERSTATUS_CANCELED;
337 break;
338
339 case VINF_SUCCESS:
[100547]340 enmSts = SHCLTRANSFERSTATUS_COMPLETED;
[100546]341 break;
342
343 default:
344 AssertFailedStmt(enmSts = SHCLTRANSFERSTATUS_ERROR);
345 break;
346 }
347
[100657]348 int rc = VbglR3ClipboardTransferSendStatus(&pCtx->CmdCtx, pCbCtx->pTransfer, enmSts, rcCompletion);
[100546]349 LogFlowFuncLeaveRC(rc);
[80845]350}
[79347]351
[100205]352/** @copydoc SHCLTRANSFERCALLBACKS::pfnOnError */
[100204]353static DECLCALLBACK(void) vbtrShClTransferErrorCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx, int rcError)
[80845]354{
[87611]355 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
[80905]356 AssertPtr(pCtx);
[79347]357
[100541]358 LogRel(("Shared Clipboard: Transfer %RU16 failed with %Rrc\n", ShClTransferGetID(pCbCtx->pTransfer), rcError));
[100546]359
360 if (g_cVerbosity) /* Only show this in verbose mode. */
361 {
362 char szMsg [256]; /* Sizes according to MSDN. */
363 char szTitle[64];
364
365 /** @todo Add some translation macros here. */
366 RTStrPrintf(szTitle, sizeof(szTitle), "VirtualBox Shared Clipboard");
367 RTStrPrintf(szMsg, sizeof(szMsg),
368 "Transfer %RU16 failed with %Rrc", ShClTransferGetID(pCbCtx->pTransfer), rcError);
369
370 hlpShowBalloonTip(g_hInstance, g_hwndToolWindow, ID_TRAYICON,
371 szMsg, szTitle,
372 5000 /* Time to display in msec */, NIIF_INFO);
373 }
374
[100657]375 int rc = VbglR3ClipboardTransferSendStatus(&pCtx->CmdCtx, pCbCtx->pTransfer, SHCLTRANSFERSTATUS_ERROR, rcError);
[100546]376 LogFlowFuncLeaveRC(rc);
[79347]377}
[80849]378#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
[79347]379
[100204]380static LRESULT vbtrShClWndProcWorker(PSHCLCONTEXT pCtx, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
[58604]381{
[26742]382 AssertPtr(pCtx);
[9]383
[80662]384 const PSHCLWINCTX pWinCtx = &pCtx->Win;
[1103]385
[78501]386 LRESULT lresultRc = 0;
[9]387
[1103]388 switch (msg)
[9]389 {
[80965]390 case WM_CLIPBOARDUPDATE:
391 {
[81025]392 LogFunc(("WM_CLIPBOARDUPDATE: pWinCtx=%p\n", pWinCtx));
[80918]393
[81134]394 if (pCtx->fShutdown) /* If we're about to shut down, skip handling stuff here. */
395 break;
396
[80965]397 int rc = RTCritSectEnter(&pWinCtx->CritSect);
398 if (RT_SUCCESS(rc))
399 {
400 const HWND hWndClipboardOwner = GetClipboardOwner();
[80918]401
[80965]402 LogFunc(("WM_CLIPBOARDUPDATE: hWndOldClipboardOwner=%p, hWndNewClipboardOwner=%p\n",
403 pWinCtx->hWndClipboardOwnerUs, hWndClipboardOwner));
[80374]404
[80965]405 if (pWinCtx->hWndClipboardOwnerUs != hWndClipboardOwner)
406 {
407 int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
408 AssertRC(rc2);
[58604]409
[80965]410 /* Clipboard was updated by another application.
411 * Report available formats to the host. */
[83624]412 SHCLFORMATS fFormats;
[103631]413 rc = ShClWinGetFormats(pWinCtx, &fFormats);
[80965]414 if (RT_SUCCESS(rc))
415 {
[83624]416 LogFunc(("WM_CLIPBOARDUPDATE: Reporting formats %#x\n", fFormats));
417 rc = VbglR3ClipboardReportFormats(pCtx->CmdCtx.idClient, fFormats);
[80965]418 }
419 }
420 else
421 {
422 int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
423 AssertRC(rc2);
424 }
425 }
426
427 if (RT_FAILURE(rc))
428 LogRel(("Shared Clipboard: WM_CLIPBOARDUPDATE failed with %Rrc\n", rc));
429
430 break;
431 }
432
[80993]433 case WM_CHANGECBCHAIN:
434 {
435 LogFunc(("WM_CHANGECBCHAIN\n"));
[103631]436 lresultRc = ShClWinHandleWMChangeCBChain(pWinCtx, hwnd, msg, wParam, lParam);
[80993]437 break;
438 }
[9]439
[80990]440 case WM_DRAWCLIPBOARD:
441 {
[81025]442 LogFlowFunc(("WM_DRAWCLIPBOARD: pWinCtx=%p\n", pWinCtx));
[9]443
[80990]444 int rc = RTCritSectEnter(&pWinCtx->CritSect);
445 if (RT_SUCCESS(rc))
446 {
447 const HWND hWndClipboardOwner = GetClipboardOwner();
[9]448
[80990]449 LogFunc(("WM_DRAWCLIPBOARD: hWndClipboardOwnerUs=%p, hWndNewClipboardOwner=%p\n",
450 pWinCtx->hWndClipboardOwnerUs, hWndClipboardOwner));
[1103]451
[80990]452 if (pWinCtx->hWndClipboardOwnerUs != hWndClipboardOwner)
453 {
454 int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
455 AssertRC(rc2);
456
457 /* Clipboard was updated by another application. */
458 /* WM_DRAWCLIPBOARD always expects a return code of 0, so don't change "rc" here. */
[83624]459 SHCLFORMATS fFormats;
[103631]460 rc = ShClWinGetFormats(pWinCtx, &fFormats);
[82513]461 if ( RT_SUCCESS(rc)
[83624]462 && fFormats != VBOX_SHCL_FMT_NONE)
463 rc = VbglR3ClipboardReportFormats(pCtx->CmdCtx.idClient, fFormats);
[80990]464 }
465 else
466 {
467 int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
468 AssertRC(rc2);
469 }
470 }
471
[103631]472 lresultRc = ShClWinChainPassToNext(pWinCtx, msg, wParam, lParam);
[80990]473 break;
474 }
475
[80993]476 case WM_TIMER:
477 {
[103631]478 int rc = ShClWinHandleWMTimer(pWinCtx);
[80993]479 AssertRC(rc);
[80374]480
[80993]481 break;
482 }
[32431]483
[80993]484 case WM_CLOSE:
485 {
486 /* Do nothing. Ignore the message. */
487 break;
488 }
[13835]489
[100204]490 case WM_RENDERFORMAT: /* Guest wants to render the clipboard data. */
[80993]491 {
492 /* Insert the requested clipboard format data into the clipboard. */
[100450]493 const UINT uFmtWin = (UINT)wParam;
[103631]494 const SHCLFORMAT uFmtVBox = ShClWinClipboardFormatToVBox(uFmtWin);
[9]495
[100450]496 LogFunc(("WM_RENDERFORMAT: uFmtWin=%u -> uFmtVBox=0x%x\n", uFmtWin, uFmtVBox));
[100204]497#ifdef LOG_ENABLED
[100450]498 char *pszFmts = ShClFormatsToStrA(uFmtVBox);
[100204]499 AssertPtrReturn(pszFmts, 0);
[100450]500 LogRel(("Shared Clipboard: Rendering Windows format %#x as VBox format '%s'\n", uFmtWin, pszFmts));
[100204]501 RTStrFree(pszFmts);
502#endif
[100450]503 if (uFmtVBox == VBOX_SHCL_FMT_NONE)
[80993]504 {
[100450]505 LogRel(("Shared Clipboard: Unsupported format (%#x) requested\n", uFmtWin));
[103631]506 ShClWinClear();
[80993]507 }
508 else
509 {
[103366]510 void *pvData = NULL;
511 uint32_t cbData;
[13835]512
[103366]513 HANDLE hMem;
514 int rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, uFmtVBox, &pvData, &cbData);
515 if (RT_SUCCESS(rc))
[80993]516 {
[103366]517 /* Verify the size of returned text, the memory block for clipboard must have the exact string size. */
518 if (uFmtVBox == VBOX_SHCL_FMT_UNICODETEXT)
[80993]519 {
[103366]520 size_t cwcActual = 0;
521 rc = RTUtf16NLenEx((PCRTUTF16)pvData, cbData / sizeof(RTUTF16), &cwcActual);
[80993]522 if (RT_SUCCESS(rc))
[103366]523 cbData = (uint32_t)((cwcActual + 1 /* '\0' */) * sizeof(RTUTF16));
524 else
525 LogRel(("Shared Clipboard: Invalid UTF16 string from host: cb=%RU32, cwcActual=%zu, rc=%Rrc\n",
526 cbData, cwcActual, rc));
527 }
528 else if (uFmtVBox == VBOX_SHCL_FMT_HTML)
529 {
530 /* Wrap content into CF_HTML clipboard format if needed. */
[103631]531 if (!ShClWinIsCFHTML((const char *)pvData))
[80993]532 {
[103366]533 char *pszWrapped = NULL;
534 uint32_t cbWrapped = 0;
[103631]535 rc = ShClWinConvertMIMEToCFHTML((const char *)pvData, cbData, &pszWrapped, &cbWrapped);
[103366]536 if (RT_SUCCESS(rc))
[80993]537 {
[103366]538 AssertBreakStmt(cbWrapped, rc = VERR_INVALID_PARAMETER);
[103367]539 pvData = RTMemRealloc(pvData, cbWrapped);
[103366]540 if (pvData)
[80993]541 {
[103366]542 memcpy(pvData, pszWrapped, cbWrapped);
543 cbData = cbWrapped;
[80993]544 }
[81768]545 else
[103366]546 rc = VERR_NO_MEMORY;
547 RTMemFree(pszWrapped);
[80993]548 }
[13835]549
[103366]550 if (RT_FAILURE(rc))
551 LogRel(("Shared Clipboard: Cannot convert HTML clipboard data into CF_HTML clipboard format, rc=%Rrc\n", rc));
552 }
553 }
[81768]554
[103366]555 hMem = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, cbData);
556 if (hMem)
557 {
558 void *pvMem = GlobalLock(hMem);
559 if (pvMem)
560 {
561 memcpy(pvMem, pvData, cbData);
562 GlobalUnlock(hMem);
[92845]563
[103366]564 HANDLE hClip = SetClipboardData(uFmtWin, hMem);
565 if (!hClip)
[80993]566 {
[103366]567 /* The hMem ownership has gone to the system. Finish the processing. */
568 break;
[80993]569 }
[103366]570 else
571 LogRel(("Shared Clipboard: Setting host data buffer to clipboard failed with %Rrc\n",
572 RTErrConvertFromWin32(GetLastError())));
[80993]573 }
[103366]574 else
575 LogRel(("Shared Clipboard: Failed to lock memory (%Rrc)\n", RTErrConvertFromWin32(GetLastError())));
576 GlobalFree(hMem);
[80993]577 }
[81768]578 else
579 LogRel(("Shared Clipboard: No memory for allocating host data buffer\n"));
[80993]580 }
581 }
[80374]582
[80993]583 break;
584 }
[9]585
[80993]586 case WM_RENDERALLFORMATS:
587 {
588 LogFunc(("WM_RENDERALLFORMATS\n"));
[79174]589
[103631]590 int rc = ShClWinHandleWMRenderAllFormats(pWinCtx, hwnd);
[80993]591 AssertRC(rc);
[80374]592
[80993]593 break;
594 }
[9]595
[100204]596 case SHCL_WIN_WM_REPORT_FORMATS: /* Host reported clipboard formats. */
[80993]597 {
598 LogFunc(("SHCL_WIN_WM_REPORT_FORMATS\n"));
[79267]599
[80993]600 /* Announce available formats. Do not insert data -- will be inserted in WM_RENDERFORMAT. */
601 PVBGLR3CLIPBOARDEVENT pEvent = (PVBGLR3CLIPBOARDEVENT)lParam;
602 AssertPtr(pEvent);
603 Assert(pEvent->enmType == VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS);
[80444]604
[83624]605 const SHCLFORMATS fFormats = pEvent->u.fReportedFormats;
[80444]606
[100204]607#ifdef LOG_ENABLED
608 char *pszFmts = ShClFormatsToStrA(fFormats);
609 AssertPtrReturn(pszFmts, 0);
610 LogRel(("Shared Clipboard: Host reported formats '%s'\n", pszFmts));
611 RTStrFree(pszFmts);
612#endif
[80993]613 if (fFormats != VBOX_SHCL_FMT_NONE) /* Could arrive with some older GA versions. */
614 {
[103631]615 int rc = ShClWinClearAndAnnounceFormats(pWinCtx, fFormats, hwnd);
[81025]616#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
[100204]617 if ( RT_SUCCESS(rc)
618 && fFormats & VBOX_SHCL_FMT_URI_LIST)
[80993]619 {
[81025]620 /*
[100394]621 * Create our IDataObject implementation and push it to the Windows clibpoard.
622 * That way Windows will recognize that there is a data transfer available.
[81025]623 */
[103631]624 ShClWinDataObject::CALLBACKS Callbacks;
[100204]625 RT_ZERO(Callbacks);
[100541]626 Callbacks.pfnTransferBegin = vbtrShClDataObjectTransferBeginCallback;
[100204]627
[103631]628 rc = ShClWinTransferCreateAndSetDataObject(pWinCtx, pCtx, &Callbacks);
[81025]629 }
[100205]630#else
631 RT_NOREF(rc);
[81025]632#endif
[80993]633 }
[80374]634
[80993]635 LogFunc(("SHCL_WIN_WM_REPORT_FORMATS: fFormats=0x%x, lastErr=%ld\n", fFormats, GetLastError()));
636 break;
637 }
[9]638
[100204]639 case SHCL_WIN_WM_READ_DATA: /* Host wants to read clipboard data from the guest. */
[80993]640 {
641 /* Send data in the specified format to the host. */
642 PVBGLR3CLIPBOARDEVENT pEvent = (PVBGLR3CLIPBOARDEVENT)lParam;
643 AssertPtr(pEvent);
644 Assert(pEvent->enmType == VBGLR3CLIPBOARDEVENTTYPE_READ_DATA);
[80444]645
[82527]646 const SHCLFORMAT fFormat = (uint32_t)pEvent->u.fReadData;
[80444]647
[82507]648 LogFlowFunc(("SHCL_WIN_WM_READ_DATA: fFormat=%#x\n", fFormat));
[100204]649#ifdef LOG_ENABLED
650 char *pszFmts = ShClFormatsToStrA(fFormat);
651 AssertPtrReturn(pszFmts, 0);
652 LogRel(("Shared Clipboard: Sending data to host as '%s'\n", pszFmts));
653 RTStrFree(pszFmts);
654#endif
[103631]655 int rc = ShClWinOpen(hwnd);
[91746]656 HANDLE hClip = NULL;
[80993]657 if (RT_SUCCESS(rc))
658 {
[82507]659 if (fFormat & VBOX_SHCL_FMT_BITMAP)
[80993]660 {
661 hClip = GetClipboardData(CF_DIB);
662 if (hClip != NULL)
663 {
664 LPVOID lp = GlobalLock(hClip);
665 if (lp != NULL)
666 {
[82880]667 rc = VbglR3ClipboardWriteDataEx(&pEvent->cmdCtx, fFormat, lp, (uint32_t)GlobalSize(hClip));
[82689]668
[80993]669 GlobalUnlock(hClip);
670 }
671 else
672 hClip = NULL;
673 }
674 }
[82507]675 else if (fFormat & VBOX_SHCL_FMT_UNICODETEXT)
[80993]676 {
677 hClip = GetClipboardData(CF_UNICODETEXT);
678 if (hClip != NULL)
679 {
680 LPWSTR uniString = (LPWSTR)GlobalLock(hClip);
681 if (uniString != NULL)
682 {
[82880]683 rc = VbglR3ClipboardWriteDataEx(&pEvent->cmdCtx,
684 fFormat, uniString, ((uint32_t)lstrlenW(uniString) + 1) * 2);
[82689]685
[80993]686 GlobalUnlock(hClip);
687 }
688 else
689 hClip = NULL;
690 }
691 }
[82507]692 else if (fFormat & VBOX_SHCL_FMT_HTML)
[80993]693 {
694 UINT format = RegisterClipboardFormat(SHCL_WIN_REGFMT_HTML);
695 if (format != 0)
696 {
697 hClip = GetClipboardData(format);
698 if (hClip != NULL)
699 {
[93396]700 LPVOID const pvClip = GlobalLock(hClip);
701 if (pvClip != NULL)
[80993]702 {
[93396]703 uint32_t const cbClip = (uint32_t)GlobalSize(hClip);
704
[92846]705 /* Unwrap clipboard content from CF_HTML format if needed. */
[103631]706 if (ShClWinIsCFHTML((const char *)pvClip))
[92846]707 {
708 char *pszBuf = NULL;
709 uint32_t cbBuf = 0;
[103631]710 rc = ShClWinConvertCFHTMLToMIME((const char *)pvClip, cbClip, &pszBuf, &cbBuf);
[92846]711 if (RT_SUCCESS(rc))
712 {
713 rc = VbglR3ClipboardWriteDataEx(&pEvent->cmdCtx, fFormat, pszBuf, cbBuf);
714 RTMemFree(pszBuf);
715 }
[93396]716 else
717 rc = VbglR3ClipboardWriteDataEx(&pEvent->cmdCtx, fFormat, pvClip, cbClip);
[92846]718 }
719 else
[93396]720 rc = VbglR3ClipboardWriteDataEx(&pEvent->cmdCtx, fFormat, pvClip, cbClip);
[92846]721
[80993]722 GlobalUnlock(hClip);
723 }
724 else
725 hClip = NULL;
726 }
727 }
728 }
[91746]729
[80993]730 if (hClip == NULL)
731 LogFunc(("SHCL_WIN_WM_READ_DATA: hClip=NULL, lastError=%ld\n", GetLastError()));
[79497]732
[103631]733 ShClWinClose();
[80993]734 }
[91746]735
736 /* If the requested clipboard format is not available, we must send empty data. */
737 if (hClip == NULL)
738 VbglR3ClipboardWriteDataEx(&pEvent->cmdCtx, VBOX_SHCL_FMT_NONE, NULL, 0);
[80993]739 break;
740 }
[79497]741
[80993]742 case WM_DESTROY:
743 {
744 LogFunc(("WM_DESTROY\n"));
[79174]745
[103631]746 int rc = ShClWinHandleWMDestroy(pWinCtx);
[80993]747 AssertRC(rc);
[79174]748
[80993]749 /*
750 * Don't need to call PostQuitMessage cause
751 * the VBoxTray already finished a message loop.
752 */
[80374]753
[80993]754 break;
755 }
[61908]756
[80993]757 default:
758 {
759 LogFunc(("WM_ %p\n", msg));
760 lresultRc = DefWindowProc(hwnd, msg, wParam, lParam);
761 break;
762 }
[9]763 }
764
[79174]765 LogFunc(("WM_ rc %d\n", lresultRc));
[78501]766 return lresultRc;
[9]767}
768
[100204]769static LRESULT CALLBACK vbtrShClWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
[9]770
[100204]771static int vbtrShClCreateWindow(PSHCLCONTEXT pCtx)
[9]772{
[57741]773 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
[9]774
[57741]775 int rc = VINF_SUCCESS;
[9]776
[57741]777 AssertPtr(pCtx->pEnv);
778 HINSTANCE hInstance = pCtx->pEnv->hInstance;
779 Assert(hInstance != 0);
[9]780
[57741]781 /* Register the Window Class. */
[81025]782 WNDCLASSEX wc;
783 RT_ZERO(wc);
[57741]784
[81025]785 wc.cbSize = sizeof(WNDCLASSEX);
786
[57741]787 if (!GetClassInfoEx(hInstance, s_szClipWndClassName, &wc))
[9]788 {
[57741]789 wc.style = CS_NOCLOSE;
[100204]790 wc.lpfnWndProc = vbtrShClWndProc;
[57741]791 wc.hInstance = pCtx->pEnv->hInstance;
792 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
793 wc.lpszClassName = s_szClipWndClassName;
794
[78151]795 ATOM wndClass = RegisterClassEx(&wc);
796 if (wndClass == 0)
[57741]797 rc = RTErrConvertFromWin32(GetLastError());
[9]798 }
[57741]799
800 if (RT_SUCCESS(rc))
[9]801 {
[80662]802 const PSHCLWINCTX pWinCtx = &pCtx->Win;
[78151]803
[9]804 /* Create the window. */
[78151]805 pWinCtx->hWnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
806 s_szClipWndClassName, s_szClipWndClassName,
807 WS_POPUPWINDOW,
808 -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
809 if (pWinCtx->hWnd == NULL)
[9]810 {
811 rc = VERR_NOT_SUPPORTED;
812 }
813 else
814 {
[78151]815 SetWindowPos(pWinCtx->hWnd, HWND_TOPMOST, -200, -200, 0, 0,
[9]816 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
817
[103631]818 rc = ShClWinChainAdd(pWinCtx);
[81025]819 if (RT_SUCCESS(rc))
820 {
[103631]821 if (!ShClWinIsNewAPI(&pWinCtx->newAPI))
[81025]822 pWinCtx->oldAPI.timerRefresh = SetTimer(pWinCtx->hWnd, 0, 10 * 1000 /* 10s */, NULL);
823 }
[9]824 }
825 }
826
[57741]827 LogFlowFuncLeaveRC(rc);
[9]828 return rc;
829}
830
[100204]831static DECLCALLBACK(int) vbtrShClWindowThread(RTTHREAD hThread, void *pvUser)
[81025]832{
833 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pvUser;
834 AssertPtr(pCtx);
835
836#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
[81027]837 HRESULT hr = OleInitialize(NULL);
[81025]838 if (FAILED(hr))
839 {
840 LogRel(("Shared Clipboard: Initializing OLE in window thread failed (%Rhrc) -- file transfers unavailable\n", hr));
841 /* Not critical, the rest of the clipboard might work. */
842 }
843 else
844 LogRel(("Shared Clipboard: Initialized OLE in window thread\n"));
[100204]845#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
[81025]846
[100204]847 int rc = vbtrShClCreateWindow(pCtx);
[81025]848 if (RT_FAILURE(rc))
849 {
850 LogRel(("Shared Clipboard: Unable to create window, rc=%Rrc\n", rc));
851 return rc;
852 }
853
854 pCtx->fStarted = true; /* Set started indicator. */
855
856 int rc2 = RTThreadUserSignal(hThread);
857 bool fSignalled = RT_SUCCESS(rc2);
858
859 LogRel2(("Shared Clipboard: Window thread running\n"));
860
861 if (RT_SUCCESS(rc))
862 {
863 for (;;)
864 {
865 MSG uMsg;
866 BOOL fRet;
867 while ((fRet = GetMessage(&uMsg, 0, 0, 0)) > 0)
868 {
869 TranslateMessage(&uMsg);
870 DispatchMessage(&uMsg);
871 }
872 Assert(fRet >= 0);
873
874 if (ASMAtomicReadBool(&pCtx->fShutdown))
875 break;
876
877 /** @todo Immediately drop on failure? */
878 }
879 }
880
881 if (!fSignalled)
882 {
883 rc2 = RTThreadUserSignal(hThread);
884 AssertRC(rc2);
885 }
886
887#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
888 OleSetClipboard(NULL); /* Make sure to flush the clipboard on destruction. */
889 OleUninitialize();
890#endif
891
892 LogRel(("Shared Clipboard: Window thread ended\n"));
893
894 LogFlowFuncLeaveRC(rc);
895 return rc;
896}
897
[100204]898static LRESULT CALLBACK vbtrShClWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
[9]899{
[80662]900 PSHCLCONTEXT pCtx = &g_Ctx; /** @todo r=andy Make pCtx available through SetWindowLongPtr() / GWL_USERDATA. */
[57741]901 AssertPtr(pCtx);
[9]902
903 /* Forward with proper context. */
[100204]904 return vbtrShClWndProcWorker(pCtx, hWnd, uMsg, wParam, lParam);
[9]905}
906
[100204]907DECLCALLBACK(int) vbtrShClInit(const PVBOXSERVICEENV pEnv, void **ppInstance)
[9]908{
[57741]909 LogFlowFuncEnter();
910
[80662]911 PSHCLCONTEXT pCtx = &g_Ctx; /* Only one instance for now. */
[57741]912 AssertPtr(pCtx);
913
914 if (pCtx->pEnv)
[9]915 {
[1103]916 /* Clipboard was already initialized. 2 or more instances are not supported. */
917 return VERR_NOT_SUPPORTED;
918 }
[9]919
[41876]920 if (VbglR3AutoLogonIsRemoteSession())
921 {
922 /* Do not use clipboard for remote sessions. */
[80845]923 LogRel(("Shared Clipboard: Clipboard has been disabled for a remote session\n"));
[41876]924 return VERR_NOT_SUPPORTED;
925 }
926
[81025]927 pCtx->pEnv = pEnv;
928 pCtx->hThread = NIL_RTTHREAD;
929 pCtx->fStarted = false;
930 pCtx->fShutdown = false;
[78725]931
[100204]932 int rc = RTReqQueueCreate(&pCtx->Win.hReqQ);
933 AssertRCReturn(rc, rc);
[78809]934
[103631]935 rc = ShClWinCtxInit(&pCtx->Win);
[100204]936 if (RT_SUCCESS(rc))
937 rc = VbglR3ClipboardConnectEx(&pCtx->CmdCtx, VBOX_SHCL_GF_0_CONTEXT_ID);
938 if (RT_SUCCESS(rc))
939 {
[80849]940#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
[100204]941 rc = ShClTransferCtxInit(&pCtx->TransferCtx);
[78474]942#endif
[57741]943 if (RT_SUCCESS(rc))
[1103]944 {
[100204]945 /* Message pump thread for our proxy window. */
946 rc = RTThreadCreate(&pCtx->hThread, vbtrShClWindowThread, pCtx /* pvUser */,
947 0, RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
948 "shclwnd");
[78809]949 if (RT_SUCCESS(rc))
950 {
[100204]951 int rc2 = RTThreadUserWait(pCtx->hThread, RT_MS_30SEC /* Timeout in ms */);
952 AssertRC(rc2);
[81025]953
[100204]954 if (!pCtx->fStarted) /* Did the thread fail to start? */
955 rc = VERR_NOT_SUPPORTED; /* Report back Shared Clipboard as not being supported. */
[78809]956 }
[100204]957 }
[81025]958
[100204]959 if (RT_SUCCESS(rc))
960 {
961 *ppInstance = pCtx;
[9]962 }
[100204]963 else
964 VbglR3ClipboardDisconnectEx(&pCtx->CmdCtx);
[9]965 }
966
[81025]967 if (RT_FAILURE(rc))
968 LogRel(("Shared Clipboard: Unable to initialize, rc=%Rrc\n", rc));
969
[57741]970 LogFlowFuncLeaveRC(rc);
[9]971 return rc;
972}
973
[100204]974DECLCALLBACK(int) vbtrShClWorker(void *pInstance, bool volatile *pfShutdown)
[9]975{
[57741]976 AssertPtr(pInstance);
977 LogFlowFunc(("pInstance=%p\n", pInstance));
[13835]978
[57741]979 /*
980 * Tell the control thread that it can continue
981 * spawning services.
982 */
983 RTThreadUserSignal(RTThreadSelf());
984
[80662]985 const PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pInstance;
[26742]986 AssertPtr(pCtx);
[13835]987
[80662]988 const PSHCLWINCTX pWinCtx = &pCtx->Win;
[78151]989
[81559]990 LogRel2(("Shared Clipboard: Worker loop running\n"));
[81025]991
992#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
993 HRESULT hr = OleInitialize(NULL);
994 if (FAILED(hr))
995 {
996 LogRel(("Shared Clipboard: Initializing OLE in worker thread failed (%Rhrc) -- file transfers unavailable\n", hr));
997 /* Not critical, the rest of the clipboard might work. */
998 }
999 else
[100090]1000 LogRel(("Shared Clipboard: Initialized OLE in worker thread\n"));
[81025]1001
[100204]1002 /*
1003 * Init callbacks.
1004 * Those will be registered within VbglR3 when a new transfer gets initialized.
1005 */
1006 RT_ZERO(pCtx->CmdCtx.Transfers.Callbacks);
1007
1008 pCtx->CmdCtx.Transfers.Callbacks.pvUser = pCtx; /* Assign context as user-provided callback data. */
1009 pCtx->CmdCtx.Transfers.Callbacks.cbUser = sizeof(SHCLCONTEXT);
1010
[100433]1011 pCtx->CmdCtx.Transfers.Callbacks.pfnOnCreated = vbtrShClTransferCreatedCallback;
1012 pCtx->CmdCtx.Transfers.Callbacks.pfnOnDestroy = vbtrShClTransferDestroyCallback;
[103442]1013 pCtx->CmdCtx.Transfers.Callbacks.pfnOnInitialize = vbtrShClTransferInitializeCallback;
[103615]1014 pCtx->CmdCtx.Transfers.Callbacks.pfnOnInitialized = vbtrShClTransferInitializedCallback;
[100204]1015 pCtx->CmdCtx.Transfers.Callbacks.pfnOnStarted = vbtrShClTransferStartedCallback;
1016 pCtx->CmdCtx.Transfers.Callbacks.pfnOnCompleted = vbtrShClTransferCompletedCallback;
1017 pCtx->CmdCtx.Transfers.Callbacks.pfnOnError = vbtrShClTransferErrorCallback;
1018#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
1019
[57741]1020 int rc;
1021
[1103]1022 /* The thread waits for incoming messages from the host. */
[100204]1023 PVBGLR3CLIPBOARDEVENT pEvent = NULL;
[9]1024 for (;;)
1025 {
[81768]1026 LogFlowFunc(("Waiting for host message (fUseLegacyProtocol=%RTbool, fHostFeatures=%#RX64) ...\n",
1027 pCtx->CmdCtx.fUseLegacyProtocol, pCtx->CmdCtx.fHostFeatures));
[80444]1028
[100204]1029 if (!pEvent)
1030 pEvent = (PVBGLR3CLIPBOARDEVENT)RTMemAllocZ(sizeof(VBGLR3CLIPBOARDEVENT));
[82527]1031 AssertPtrBreakStmt(pEvent, rc = VERR_NO_MEMORY);
[57741]1032
[82527]1033 uint32_t idMsg = 0;
1034 uint32_t cParms = 0;
[100407]1035 rc = VbglR3ClipboardMsgPeekWait(&pCtx->CmdCtx, &idMsg, &cParms, NULL /* pidRestoreCheck */);
[82527]1036 if (RT_SUCCESS(rc))
[80444]1037 {
[80849]1038#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
[82527]1039 rc = VbglR3ClipboardEventGetNextEx(idMsg, cParms, &pCtx->CmdCtx, &pCtx->TransferCtx, pEvent);
[80845]1040#else
[82527]1041 rc = VbglR3ClipboardEventGetNext(idMsg, cParms, &pCtx->CmdCtx, pEvent);
[80845]1042#endif
[80444]1043 }
[100204]1044 else if (rc == VERR_TRY_AGAIN) /* No new message (yet). */
1045 {
1046 RTReqQueueProcess(pCtx->Win.hReqQ, RT_MS_1SEC);
1047 continue;
1048 }
[80444]1049
1050 if (RT_FAILURE(rc))
1051 {
[81025]1052 LogFlowFunc(("Getting next event failed with %Rrc\n", rc));
1053
[80845]1054 VbglR3ClipboardEventFree(pEvent);
1055 pEvent = NULL;
1056
[57741]1057 if (*pfShutdown)
1058 break;
1059
[9]1060 /* Wait a bit before retrying. */
[57741]1061 RTThreadSleep(1000);
[9]1062 continue;
[43359]1063 }
[26742]1064 else
[9]1065 {
[80444]1066 AssertPtr(pEvent);
1067 LogFlowFunc(("Event uType=%RU32\n", pEvent->enmType));
1068
1069 switch (pEvent->enmType)
[26742]1070 {
[80993]1071 case VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS:
1072 {
1073 /* The host has announced available clipboard formats.
1074 * Forward the information to the window, so it can later
1075 * respond to WM_RENDERFORMAT message. */
1076 ::PostMessage(pWinCtx->hWnd, SHCL_WIN_WM_REPORT_FORMATS,
1077 0 /* wParam */, (LPARAM)pEvent /* lParam */);
[80990]1078
[80993]1079 pEvent = NULL; /* Consume pointer. */
1080 break;
1081 }
[9]1082
[80993]1083 case VBGLR3CLIPBOARDEVENTTYPE_READ_DATA:
1084 {
1085 /* The host needs data in the specified format. */
1086 ::PostMessage(pWinCtx->hWnd, SHCL_WIN_WM_READ_DATA,
1087 0 /* wParam */, (LPARAM)pEvent /* lParam */);
[80990]1088
[80993]1089 pEvent = NULL; /* Consume pointer. */
1090 break;
1091 }
[80845]1092
[80993]1093 case VBGLR3CLIPBOARDEVENTTYPE_QUIT:
1094 {
[81025]1095 LogRel2(("Shared Clipboard: Host requested termination\n"));
[80993]1096 ASMAtomicXchgBool(pfShutdown, true);
1097 break;
1098 }
[9]1099
[80991]1100#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
[81025]1101 case VBGLR3CLIPBOARDEVENTTYPE_TRANSFER_STATUS:
[80993]1102 {
1103 /* Nothing to do here. */
[81025]1104 rc = VINF_SUCCESS;
[80993]1105 break;
1106 }
[80991]1107#endif
[81025]1108 case VBGLR3CLIPBOARDEVENTTYPE_NONE:
[80993]1109 {
[81025]1110 /* Nothing to do here. */
1111 rc = VINF_SUCCESS;
[80993]1112 break;
1113 }
[81025]1114
1115 default:
1116 {
1117 AssertMsgFailedBreakStmt(("Event type %RU32 not implemented\n", pEvent->enmType), rc = VERR_NOT_SUPPORTED);
1118 }
[9]1119 }
[80444]1120
[80990]1121 if (pEvent)
1122 {
[80444]1123 VbglR3ClipboardEventFree(pEvent);
[80990]1124 pEvent = NULL;
1125 }
[9]1126 }
[57741]1127
1128 if (*pfShutdown)
1129 break;
[9]1130 }
[57741]1131
[81960]1132 LogRel2(("Shared Clipboard: Worker loop ended\n"));
[81025]1133
[80918]1134#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1135 OleSetClipboard(NULL); /* Make sure to flush the clipboard on destruction. */
1136 OleUninitialize();
1137#endif
1138
[57741]1139 LogFlowFuncLeaveRC(rc);
1140 return rc;
[9]1141}
1142
[100204]1143DECLCALLBACK(int) vbtrShClStop(void *pInstance)
[9]1144{
[57741]1145 AssertPtrReturn(pInstance, VERR_INVALID_POINTER);
[9]1146
[57741]1147 LogFunc(("Stopping pInstance=%p\n", pInstance));
1148
[80662]1149 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pInstance;
[57741]1150 AssertPtr(pCtx);
1151
[81134]1152 /* Set shutdown indicator. */
1153 ASMAtomicWriteBool(&pCtx->fShutdown, true);
1154
1155 /* Let our clipboard know that we're going to shut down. */
1156 PostMessage(pCtx->Win.hWnd, WM_QUIT, 0, 0);
1157
1158 /* Disconnect from the host service.
[81369]1159 * This will also send a VBOX_SHCL_HOST_MSG_QUIT from the host so that we can break out from our message worker. */
[82525]1160 VbglR3ClipboardDisconnect(pCtx->CmdCtx.idClient);
1161 pCtx->CmdCtx.idClient = 0;
[57741]1162
1163 LogFlowFuncLeaveRC(VINF_SUCCESS);
1164 return VINF_SUCCESS;
1165}
1166
[100204]1167DECLCALLBACK(void) vbtrShClDestroy(void *pInstance)
[57741]1168{
1169 AssertPtrReturnVoid(pInstance);
1170
[80662]1171 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pInstance;
[100204]1172 AssertPtrReturnVoid(pCtx);
[57741]1173
1174 /* Make sure that we are disconnected. */
[82525]1175 Assert(pCtx->CmdCtx.idClient == 0);
[57741]1176
[100204]1177 LogFlowFunc(("pCtx=%p\n", pCtx));
[57741]1178
[100204]1179 LogRel2(("Shared Clipboard: Destroying ...\n"));
1180
1181 const PSHCLWINCTX pWinCtx = &pCtx->Win;
1182
1183 if (pCtx->hThread != NIL_RTTHREAD)
1184 {
1185 int rcThread = VERR_WRONG_ORDER;
1186 int rc = RTThreadWait(pCtx->hThread, 60 * 1000 /* Timeout in ms */, &rcThread);
1187 LogFlowFunc(("Waiting for thread resulted in %Rrc (thread exited with %Rrc)\n",
1188 rc, rcThread));
1189 RT_NOREF(rc);
1190 }
1191
1192 if (pWinCtx->hWnd)
1193 {
1194 DestroyWindow(pWinCtx->hWnd);
1195 pWinCtx->hWnd = NULL;
1196 }
1197
1198 UnregisterClass(s_szClipWndClassName, pCtx->pEnv->hInstance);
1199
[103631]1200 ShClWinCtxDestroy(&pCtx->Win);
[100204]1201
[80849]1202#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
[81223]1203 ShClTransferCtxDestroy(&pCtx->TransferCtx);
[78474]1204#endif
1205
[100204]1206 RTReqQueueDestroy(pCtx->Win.hReqQ);
1207
1208 LogRel2(("Shared Clipboard: Destroyed\n"));
1209
[9]1210 return;
1211}
[26742]1212
[57741]1213/**
1214 * The service description.
1215 */
1216VBOXSERVICEDESC g_SvcDescClipboard =
1217{
1218 /* pszName. */
1219 "clipboard",
1220 /* pszDescription. */
1221 "Shared Clipboard",
1222 /* methods */
[100204]1223 vbtrShClInit,
1224 vbtrShClWorker,
1225 vbtrShClStop,
1226 vbtrShClDestroy
[57741]1227};
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use