VirtualBox

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

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

Shared Clipboard: Windows host service now uses the same way as the Linux host service wrt transfer handling. ​​​bugref:9437

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