VirtualBox

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

Last change on this file since 100541 was 100541, checked in by vboxsync, 17 months ago

Shared Clipboard: Renamed SharedClipboardWinDataObject::CALLBACKS::pfnTransferStart -> pfnTransferBegin + added pfnTransferEnd, added data object callbacks to propagate transfer end status. bugref:9437

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