VirtualBox

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

Last change on this file since 102987 was 100657, checked in by vboxsync, 19 months ago

Shared Clipboard/VbglR3: Renamed VbglR3ClipboardTransferStatusReply() -> VbglR3ClipboardTransferSendStatus(), as the function can be also called without any former call from the host. bugref:9437

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette