VirtualBox

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

最後變更 在這個檔案從103363是 103363,由 vboxsync 提交於 14 月 前

Shared Clipboard: Added a dedicated VERR_SHCLPB_NO_DATA error code, to indicate that clipboard data for a format currently is not available.

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

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