VirtualBox

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

Last change on this file since 106411 was 106411, checked in by vboxsync, 3 months ago

Additions/VBoxTray: Implemented ability for easier user-controllable logging (also via verbose levels), support for running in foreground mode (with a console window attached to) and selective starting of sub services to easier pinpoint errors in release builds. Cleaned up initialization / termination code a little. See command line help for new options. bugref:10763

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