VirtualBox

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

Last change on this file since 88801 was 87611, checked in by vboxsync, 4 years ago

Shared Clipboard/Transfers: More callback code. ​bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 39.4 KB
Line 
1/* $Id: VBoxClipboard.cpp 87611 2021-02-04 16:31:28Z vboxsync $ */
2/** @file
3 * VBoxClipboard - Shared clipboard, Windows Guest Implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
23#include <VBox/log.h>
24
25#include "VBoxTray.h"
26#include "VBoxHelpers.h"
27
28#include <iprt/asm.h>
29#include <iprt/errcore.h>
30#include <iprt/mem.h>
31#include <iprt/ldr.h>
32
33
34#include <VBox/GuestHost/SharedClipboard.h>
35#include <VBox/GuestHost/SharedClipboard-win.h>
36#include <VBox/GuestHost/clipboard-helper.h>
37#include <VBox/HostServices/VBoxClipboardSvc.h> /* Temp, remove. */
38#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
39# include <VBox/GuestHost/SharedClipboard-transfers.h>
40#endif
41
42#include <strsafe.h>
43
44#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
45# include <iprt/win/shlobj.h>
46# include <iprt/win/shlwapi.h>
47#endif
48
49
50/*********************************************************************************************************************************
51* Structures and Typedefs *
52*********************************************************************************************************************************/
53struct SHCLCONTEXT
54{
55 /** Pointer to the VBoxClient service environment. */
56 const VBOXSERVICEENV *pEnv;
57 /** Command context. */
58 VBGLR3SHCLCMDCTX CmdCtx;
59 /** Windows-specific context data. */
60 SHCLWINCTX Win;
61 /** Thread handle for window thread. */
62 RTTHREAD hThread;
63 /** Start indicator flag. */
64 bool fStarted;
65 /** Shutdown indicator flag. */
66 bool fShutdown;
67#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
68 /** Associated transfer data. */
69 SHCLTRANSFERCTX TransferCtx;
70#endif
71};
72
73
74/*********************************************************************************************************************************
75* Static variables *
76*********************************************************************************************************************************/
77/** Static clipboard context (since it is the single instance). Directly used in the windows proc. */
78static SHCLCONTEXT g_Ctx = { NULL };
79/** Static window class name. */
80static char s_szClipWndClassName[] = SHCL_WIN_WNDCLASS_NAME;
81
82
83/*********************************************************************************************************************************
84* Prototypes *
85*********************************************************************************************************************************/
86#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
87static DECLCALLBACK(int) vboxClipboardOnTransferInitCallback(PSHCLTXPROVIDERCTX pCtx);
88static DECLCALLBACK(int) vboxClipboardOnTransferStartCallback(PSHCLTXPROVIDERCTX pCtx);
89static DECLCALLBACK(void) vboxClipboardOnTransferCompleteCallback(PSHCLTXPROVIDERCTX pCtx, int rc);
90static DECLCALLBACK(void) vboxClipboardOnTransferErrorCallback(PSHCLTXPROVIDERCTX pCtx, int rc);
91#endif
92
93
94#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
95
96/**
97 * Cleanup helper function for transfer callbacks.
98 *
99 * @param pTransferCtx Pointer to transfer context that the transfer contains.
100 * @param pTransfer Pointer to transfer to cleanup.
101 */
102static void vboxClipboardTransferCallbackCleanup(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer)
103{
104 LogFlowFuncEnter();
105
106 if (!pTransferCtx || !pTransfer)
107 return;
108
109 if (pTransfer->pvUser) /* SharedClipboardWinTransferCtx */
110 {
111 delete pTransfer->pvUser;
112 pTransfer->pvUser = NULL;
113 }
114
115 int rc2 = ShClTransferCtxTransferUnregister(pTransferCtx, pTransfer->State.uID);
116 AssertRC(rc2);
117
118 ShClTransferDestroy(pTransfer);
119
120 RTMemFree(pTransfer);
121 pTransfer = NULL;
122}
123
124/** @copydoc SHCLTRANSFERCALLBACKTABLE::pfnOnInitialize */
125static DECLCALLBACK(int) vboxClipboardOnTransferInitCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
126{
127 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
128 AssertPtr(pCtx);
129
130 LogFlowFunc(("pCtx=%p\n", pCtx));
131
132 RT_NOREF(pCtx);
133
134 return VINF_SUCCESS;
135}
136
137/** @copydoc SHCLTRANSFERCALLBACKTABLE::pfnOnStart */
138static DECLCALLBACK(int) vboxClipboardOnTransferStartCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx)
139{
140 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
141 AssertPtr(pCtx);
142
143 PSHCLTRANSFER pTransfer = pCbCtx->pTransfer;
144 AssertPtr(pTransfer);
145
146 const SHCLTRANSFERDIR enmDir = ShClTransferGetDir(pTransfer);
147
148 LogFlowFunc(("pCtx=%p, idTransfer=%RU32, enmDir=%RU32\n", pCtx, ShClTransferGetID(pTransfer), enmDir));
149
150 int rc;
151
152 /* The guest wants to write local data to the host? */
153 if (enmDir == SHCLTRANSFERDIR_TO_REMOTE)
154 {
155 rc = SharedClipboardWinGetRoots(&pCtx->Win, pTransfer);
156 }
157 /* The guest wants to read data from a remote source. */
158 else if (enmDir == SHCLTRANSFERDIR_FROM_REMOTE)
159 {
160 /* The IDataObject *must* be created on the same thread as our (proxy) window, so post a message to it
161 * to do the stuff for us. */
162 const SHCLEVENTID idEvent = ShClEventIdGenerateAndRegister(&pTransfer->Events);
163 if (idEvent != NIL_SHCLEVENTID)
164 {
165 /* Don't want to rely on SendMessage (synchronous) here, so just post and wait the event getting signalled. */
166 ::PostMessage(pCtx->Win.hWnd, SHCL_WIN_WM_TRANSFER_START, (WPARAM)pTransfer, (LPARAM)idEvent);
167
168 PSHCLEVENTPAYLOAD pPayload;
169 rc = ShClEventWait(&pTransfer->Events, idEvent, 30 * 1000 /* Timeout in ms */, &pPayload);
170 if (RT_SUCCESS(rc))
171 {
172 Assert(pPayload->cbData == sizeof(int));
173 rc = *(int *)pPayload->pvData;
174
175 ShClPayloadFree(pPayload);
176 }
177
178 ShClEventUnregister(&pTransfer->Events, idEvent);
179 }
180 else
181 AssertFailedStmt(rc = VERR_SHCLPB_MAX_EVENTS_REACHED);
182 }
183 else
184 AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
185
186 if (RT_FAILURE(rc))
187 LogRel(("Shared Clipboard: Starting transfer failed, rc=%Rrc\n", rc));
188
189 LogFlowFunc(("LEAVE: idTransfer=%RU32, rc=%Rrc\n", ShClTransferGetID(pTransfer), rc));
190 return rc;
191}
192
193/** @copydoc SHCLTRANSFERCALLBACKTABLE::pfnOnCompleted */
194static DECLCALLBACK(void) vboxClipboardOnTransferCompletedCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx, int rcCompletion)
195{
196 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
197 AssertPtr(pCtx);
198
199 LogRel2(("Shared Clipboard: Transfer to destination %s\n",
200 rcCompletion == VERR_CANCELLED ? "canceled" : "complete"));
201
202 vboxClipboardTransferCallbackCleanup(&pCtx->TransferCtx, pCbCtx->pTransfer);
203}
204
205/** @copydoc SHCLTRANSFERCALLBACKTABLE::pfnOnError */
206static DECLCALLBACK(void) vboxClipboardOnTransferErrorCallback(PSHCLTRANSFERCALLBACKCTX pCbCtx, int rcError)
207{
208 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pCbCtx->pvUser;
209 AssertPtr(pCtx);
210
211 LogRel(("Shared Clipboard: Transfer to destination failed with %Rrc\n", rcError));
212
213 vboxClipboardTransferCallbackCleanup(&pCtx->TransferCtx, pCbCtx->pTransfer);
214}
215
216#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */
217
218static LRESULT vboxClipboardWinProcessMsg(PSHCLCONTEXT pCtx, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
219{
220 AssertPtr(pCtx);
221
222 const PSHCLWINCTX pWinCtx = &pCtx->Win;
223
224 LRESULT lresultRc = 0;
225
226 switch (msg)
227 {
228 case WM_CLIPBOARDUPDATE:
229 {
230 LogFunc(("WM_CLIPBOARDUPDATE: pWinCtx=%p\n", pWinCtx));
231
232 if (pCtx->fShutdown) /* If we're about to shut down, skip handling stuff here. */
233 break;
234
235 int rc = RTCritSectEnter(&pWinCtx->CritSect);
236 if (RT_SUCCESS(rc))
237 {
238 const HWND hWndClipboardOwner = GetClipboardOwner();
239
240 LogFunc(("WM_CLIPBOARDUPDATE: hWndOldClipboardOwner=%p, hWndNewClipboardOwner=%p\n",
241 pWinCtx->hWndClipboardOwnerUs, hWndClipboardOwner));
242
243 if (pWinCtx->hWndClipboardOwnerUs != hWndClipboardOwner)
244 {
245 int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
246 AssertRC(rc2);
247
248 /* Clipboard was updated by another application.
249 * Report available formats to the host. */
250 SHCLFORMATS fFormats;
251 rc = SharedClipboardWinGetFormats(pWinCtx, &fFormats);
252 if (RT_SUCCESS(rc))
253 {
254 LogFunc(("WM_CLIPBOARDUPDATE: Reporting formats %#x\n", fFormats));
255 rc = VbglR3ClipboardReportFormats(pCtx->CmdCtx.idClient, fFormats);
256 }
257 }
258 else
259 {
260 int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
261 AssertRC(rc2);
262 }
263 }
264
265 if (RT_FAILURE(rc))
266 LogRel(("Shared Clipboard: WM_CLIPBOARDUPDATE failed with %Rrc\n", rc));
267
268 break;
269 }
270
271 case WM_CHANGECBCHAIN:
272 {
273 LogFunc(("WM_CHANGECBCHAIN\n"));
274 lresultRc = SharedClipboardWinHandleWMChangeCBChain(pWinCtx, hwnd, msg, wParam, lParam);
275 break;
276 }
277
278 case WM_DRAWCLIPBOARD:
279 {
280 LogFlowFunc(("WM_DRAWCLIPBOARD: pWinCtx=%p\n", pWinCtx));
281
282 int rc = RTCritSectEnter(&pWinCtx->CritSect);
283 if (RT_SUCCESS(rc))
284 {
285 const HWND hWndClipboardOwner = GetClipboardOwner();
286
287 LogFunc(("WM_DRAWCLIPBOARD: hWndClipboardOwnerUs=%p, hWndNewClipboardOwner=%p\n",
288 pWinCtx->hWndClipboardOwnerUs, hWndClipboardOwner));
289
290 if (pWinCtx->hWndClipboardOwnerUs != hWndClipboardOwner)
291 {
292 int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
293 AssertRC(rc2);
294
295 /* Clipboard was updated by another application. */
296 /* WM_DRAWCLIPBOARD always expects a return code of 0, so don't change "rc" here. */
297 SHCLFORMATS fFormats;
298 rc = SharedClipboardWinGetFormats(pWinCtx, &fFormats);
299 if ( RT_SUCCESS(rc)
300 && fFormats != VBOX_SHCL_FMT_NONE)
301 rc = VbglR3ClipboardReportFormats(pCtx->CmdCtx.idClient, fFormats);
302 }
303 else
304 {
305 int rc2 = RTCritSectLeave(&pWinCtx->CritSect);
306 AssertRC(rc2);
307 }
308 }
309
310 lresultRc = SharedClipboardWinChainPassToNext(pWinCtx, msg, wParam, lParam);
311 break;
312 }
313
314 case WM_TIMER:
315 {
316 int rc = SharedClipboardWinHandleWMTimer(pWinCtx);
317 AssertRC(rc);
318
319 break;
320 }
321
322 case WM_CLOSE:
323 {
324 /* Do nothing. Ignore the message. */
325 break;
326 }
327
328 case WM_RENDERFORMAT:
329 {
330 LogFunc(("WM_RENDERFORMAT\n"));
331
332 /* Insert the requested clipboard format data into the clipboard. */
333 const UINT cfFormat = (UINT)wParam;
334
335 const SHCLFORMAT fFormat = SharedClipboardWinClipboardFormatToVBox(cfFormat);
336
337 LogFunc(("WM_RENDERFORMAT: cfFormat=%u -> fFormat=0x%x\n", cfFormat, fFormat));
338
339 if (fFormat == VBOX_SHCL_FMT_NONE)
340 {
341 LogFunc(("WM_RENDERFORMAT: Unsupported format requested\n"));
342 SharedClipboardWinClear();
343 }
344 else
345 {
346 const uint32_t cbPrealloc = _4K;
347 uint32_t cb = 0;
348
349 /* Preallocate a buffer, most of small text transfers will fit into it. */
350 HANDLE hMem = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, cbPrealloc);
351 if (hMem)
352 {
353 void *pMem = GlobalLock(hMem);
354 if (pMem)
355 {
356 /* Read the host data to the preallocated buffer. */
357 int rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, fFormat, pMem, cbPrealloc, &cb);
358 if (RT_SUCCESS(rc))
359 {
360 if (cb == 0)
361 {
362 /* 0 bytes returned means the clipboard is empty.
363 * Deallocate the memory and set hMem to NULL to get to
364 * the clipboard empty code path. */
365 GlobalUnlock(hMem);
366 GlobalFree(hMem);
367 hMem = NULL;
368 }
369 else if (cb > cbPrealloc)
370 {
371 GlobalUnlock(hMem);
372
373 LogRel2(("Shared Clipboard: Buffer too small (%RU32), needs %RU32 bytes\n", cbPrealloc, cb));
374
375 /* The preallocated buffer is too small, adjust the size. */
376 hMem = GlobalReAlloc(hMem, cb, 0);
377 if (hMem)
378 {
379 pMem = GlobalLock(hMem);
380 if (pMem)
381 {
382 /* Read the host data to the preallocated buffer. */
383 uint32_t cbNew = 0;
384 rc = VbglR3ClipboardReadDataEx(&pCtx->CmdCtx, fFormat, pMem, cb, &cbNew);
385 if ( RT_SUCCESS(rc)
386 && cbNew <= cb)
387 {
388 cb = cbNew;
389 }
390 else
391 {
392 LogRel(("Shared Clipboard: Receiving host data failed with %Rrc\n", rc));
393
394 GlobalUnlock(hMem);
395 GlobalFree(hMem);
396 hMem = NULL;
397 }
398 }
399 else
400 {
401 LogRel(("Shared Clipboard: Error locking reallocated host data buffer\n"));
402
403 GlobalFree(hMem);
404 hMem = NULL;
405 }
406 }
407 else
408 LogRel(("Shared Clipboard: No memory for reallocating host data buffer\n"));
409 }
410
411 if (hMem)
412 {
413 /* pMem is the address of the data. cb is the size of returned data. */
414 /* Verify the size of returned text, the memory block for clipboard
415 * must have the exact string size.
416 */
417 if (fFormat == VBOX_SHCL_FMT_UNICODETEXT)
418 {
419 size_t cbActual = 0;
420 HRESULT hrc = StringCbLengthW((LPWSTR)pMem, cb, &cbActual);
421 if (FAILED(hrc))
422 {
423 LogRel(("Shared Clipboard: Received host data is invalid (%RU32 vs. %zu)\n",
424 cb, cbActual));
425
426 /* Discard invalid data. */
427 GlobalUnlock(hMem);
428 GlobalFree(hMem);
429 hMem = NULL;
430 }
431 else
432 {
433 /* cbActual is the number of bytes, excluding those used
434 * for the terminating null character.
435 */
436 cb = (uint32_t)(cbActual + 2);
437 }
438 }
439 }
440
441 if (hMem)
442 {
443 GlobalUnlock(hMem);
444
445 hMem = GlobalReAlloc(hMem, cb, 0);
446 if (hMem)
447 {
448 /* 'hMem' contains the host clipboard data.
449 * size is 'cb' and format is 'format'. */
450 HANDLE hClip = SetClipboardData(cfFormat, hMem);
451 if (hClip)
452 {
453 /* The hMem ownership has gone to the system. Finish the processing. */
454 break;
455 }
456 else
457 LogRel(("Shared Clipboard: Setting host data buffer to clipboard failed with %ld\n",
458 GetLastError()));
459
460 /* Cleanup follows. */
461 }
462 else
463 LogRel(("Shared Clipboard: No memory for allocating final host data buffer\n"));
464 }
465 }
466
467 if (hMem)
468 GlobalUnlock(hMem);
469 }
470 else
471 LogRel(("Shared Clipboard: No memory for allocating host data buffer\n"));
472
473 if (hMem)
474 GlobalFree(hMem);
475 }
476 }
477
478 break;
479 }
480
481 case WM_RENDERALLFORMATS:
482 {
483 LogFunc(("WM_RENDERALLFORMATS\n"));
484
485 int rc = SharedClipboardWinHandleWMRenderAllFormats(pWinCtx, hwnd);
486 AssertRC(rc);
487
488 break;
489 }
490
491 case SHCL_WIN_WM_REPORT_FORMATS:
492 {
493 LogFunc(("SHCL_WIN_WM_REPORT_FORMATS\n"));
494
495 /* Announce available formats. Do not insert data -- will be inserted in WM_RENDERFORMAT. */
496 PVBGLR3CLIPBOARDEVENT pEvent = (PVBGLR3CLIPBOARDEVENT)lParam;
497 AssertPtr(pEvent);
498 Assert(pEvent->enmType == VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS);
499
500 const SHCLFORMATS fFormats = pEvent->u.fReportedFormats;
501
502 if (fFormats != VBOX_SHCL_FMT_NONE) /* Could arrive with some older GA versions. */
503 {
504#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
505 if (fFormats & VBOX_SHCL_FMT_URI_LIST)
506 {
507 LogFunc(("VBOX_SHCL_FMT_URI_LIST\n"));
508
509 /*
510 * Creating and starting the actual transfer will be done in vbglR3ClipboardTransferStart() as soon
511 * as the host announces the start of the transfer via a VBOX_SHCL_HOST_MSG_TRANSFER_STATUS message.
512 * Transfers always are controlled and initiated on the host side!
513 *
514 * So don't announce the transfer to the OS here yet. Don't touch the clipboard in any here; otherwise
515 * this will trigger a WM_DRAWCLIPBOARD or friends, which will result in fun bugs coming up.
516 */
517 }
518 else
519 {
520#endif
521 int rc = SharedClipboardWinOpen(hwnd);
522 if (RT_SUCCESS(rc))
523 {
524 SharedClipboardWinClear();
525
526 rc = SharedClipboardWinAnnounceFormats(pWinCtx, fFormats);
527 }
528
529 SharedClipboardWinClose();
530
531#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
532 }
533#endif
534 }
535
536 LogFunc(("SHCL_WIN_WM_REPORT_FORMATS: fFormats=0x%x, lastErr=%ld\n", fFormats, GetLastError()));
537 break;
538 }
539
540 case SHCL_WIN_WM_READ_DATA:
541 {
542 /* Send data in the specified format to the host. */
543 PVBGLR3CLIPBOARDEVENT pEvent = (PVBGLR3CLIPBOARDEVENT)lParam;
544 AssertPtr(pEvent);
545 Assert(pEvent->enmType == VBGLR3CLIPBOARDEVENTTYPE_READ_DATA);
546
547 const SHCLFORMAT fFormat = (uint32_t)pEvent->u.fReadData;
548
549 HANDLE hClip = NULL;
550
551 LogFlowFunc(("SHCL_WIN_WM_READ_DATA: fFormat=%#x\n", fFormat));
552
553 int rc = SharedClipboardWinOpen(hwnd);
554 if (RT_SUCCESS(rc))
555 {
556 if (fFormat & VBOX_SHCL_FMT_BITMAP)
557 {
558 hClip = GetClipboardData(CF_DIB);
559 if (hClip != NULL)
560 {
561 LPVOID lp = GlobalLock(hClip);
562 if (lp != NULL)
563 {
564 rc = VbglR3ClipboardWriteDataEx(&pEvent->cmdCtx, fFormat, lp, (uint32_t)GlobalSize(hClip));
565
566 GlobalUnlock(hClip);
567 }
568 else
569 {
570 hClip = NULL;
571 }
572 }
573 }
574 else if (fFormat & VBOX_SHCL_FMT_UNICODETEXT)
575 {
576 hClip = GetClipboardData(CF_UNICODETEXT);
577 if (hClip != NULL)
578 {
579 LPWSTR uniString = (LPWSTR)GlobalLock(hClip);
580 if (uniString != NULL)
581 {
582 rc = VbglR3ClipboardWriteDataEx(&pEvent->cmdCtx,
583 fFormat, uniString, ((uint32_t)lstrlenW(uniString) + 1) * 2);
584
585 GlobalUnlock(hClip);
586 }
587 else
588 {
589 hClip = NULL;
590 }
591 }
592 }
593 else if (fFormat & VBOX_SHCL_FMT_HTML)
594 {
595 UINT format = RegisterClipboardFormat(SHCL_WIN_REGFMT_HTML);
596 if (format != 0)
597 {
598 hClip = GetClipboardData(format);
599 if (hClip != NULL)
600 {
601 LPVOID lp = GlobalLock(hClip);
602 if (lp != NULL)
603 {
604 rc = VbglR3ClipboardWriteDataEx(&pEvent->cmdCtx, fFormat, lp, (uint32_t)GlobalSize(hClip));
605
606 GlobalUnlock(hClip);
607 }
608 else
609 {
610 hClip = NULL;
611 }
612 }
613 }
614 }
615 if (hClip == NULL)
616 {
617 LogFunc(("SHCL_WIN_WM_READ_DATA: hClip=NULL, lastError=%ld\n", GetLastError()));
618
619 /* Requested clipboard format is not available, send empty data. */
620 VbglR3ClipboardWriteData(pCtx->CmdCtx.idClient, VBOX_SHCL_FMT_NONE, NULL, 0);
621 }
622
623 SharedClipboardWinClose();
624 }
625 break;
626 }
627
628#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
629 case SHCL_WIN_WM_TRANSFER_START:
630 {
631 LogFunc(("SHCL_WIN_WM_TRANSFER_START\n"));
632
633 PSHCLTRANSFER pTransfer = (PSHCLTRANSFER)wParam;
634 AssertPtr(pTransfer);
635
636 const SHCLEVENTID idEvent = (SHCLEVENTID)lParam;
637
638 Assert(ShClTransferGetSource(pTransfer) == SHCLSOURCE_REMOTE); /* Sanity. */
639
640 int rcTransfer = SharedClipboardWinTransferCreate(pWinCtx, pTransfer);
641
642 PSHCLEVENTPAYLOAD pPayload = NULL;
643 int rc = ShClPayloadAlloc(idEvent, &rcTransfer, sizeof(rcTransfer), &pPayload);
644 if (RT_SUCCESS(rc))
645 {
646 rc = ShClEventSignal(&pTransfer->Events, idEvent, pPayload);
647 if (RT_FAILURE(rc))
648 ShClPayloadFree(pPayload);
649 }
650
651 break;
652 }
653#endif
654 case WM_DESTROY:
655 {
656 LogFunc(("WM_DESTROY\n"));
657
658 int rc = SharedClipboardWinHandleWMDestroy(pWinCtx);
659 AssertRC(rc);
660
661 /*
662 * Don't need to call PostQuitMessage cause
663 * the VBoxTray already finished a message loop.
664 */
665
666 break;
667 }
668
669 default:
670 {
671 LogFunc(("WM_ %p\n", msg));
672 lresultRc = DefWindowProc(hwnd, msg, wParam, lParam);
673 break;
674 }
675 }
676
677 LogFunc(("WM_ rc %d\n", lresultRc));
678 return lresultRc;
679}
680
681static LRESULT CALLBACK vboxClipboardWinWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
682
683static int vboxClipboardCreateWindow(PSHCLCONTEXT pCtx)
684{
685 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
686
687 int rc = VINF_SUCCESS;
688
689 AssertPtr(pCtx->pEnv);
690 HINSTANCE hInstance = pCtx->pEnv->hInstance;
691 Assert(hInstance != 0);
692
693 /* Register the Window Class. */
694 WNDCLASSEX wc;
695 RT_ZERO(wc);
696
697 wc.cbSize = sizeof(WNDCLASSEX);
698
699 if (!GetClassInfoEx(hInstance, s_szClipWndClassName, &wc))
700 {
701 wc.style = CS_NOCLOSE;
702 wc.lpfnWndProc = vboxClipboardWinWndProc;
703 wc.hInstance = pCtx->pEnv->hInstance;
704 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
705 wc.lpszClassName = s_szClipWndClassName;
706
707 ATOM wndClass = RegisterClassEx(&wc);
708 if (wndClass == 0)
709 rc = RTErrConvertFromWin32(GetLastError());
710 }
711
712 if (RT_SUCCESS(rc))
713 {
714 const PSHCLWINCTX pWinCtx = &pCtx->Win;
715
716 /* Create the window. */
717 pWinCtx->hWnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
718 s_szClipWndClassName, s_szClipWndClassName,
719 WS_POPUPWINDOW,
720 -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
721 if (pWinCtx->hWnd == NULL)
722 {
723 rc = VERR_NOT_SUPPORTED;
724 }
725 else
726 {
727 SetWindowPos(pWinCtx->hWnd, HWND_TOPMOST, -200, -200, 0, 0,
728 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
729
730 rc = SharedClipboardWinChainAdd(pWinCtx);
731 if (RT_SUCCESS(rc))
732 {
733 if (!SharedClipboardWinIsNewAPI(&pWinCtx->newAPI))
734 pWinCtx->oldAPI.timerRefresh = SetTimer(pWinCtx->hWnd, 0, 10 * 1000 /* 10s */, NULL);
735 }
736 }
737 }
738
739 LogFlowFuncLeaveRC(rc);
740 return rc;
741}
742
743static DECLCALLBACK(int) vboxClipboardWindowThread(RTTHREAD hThread, void *pvUser)
744{
745 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pvUser;
746 AssertPtr(pCtx);
747
748#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
749 HRESULT hr = OleInitialize(NULL);
750 if (FAILED(hr))
751 {
752 LogRel(("Shared Clipboard: Initializing OLE in window thread failed (%Rhrc) -- file transfers unavailable\n", hr));
753 /* Not critical, the rest of the clipboard might work. */
754 }
755 else
756 LogRel(("Shared Clipboard: Initialized OLE in window thread\n"));
757#endif
758
759 int rc = vboxClipboardCreateWindow(pCtx);
760 if (RT_FAILURE(rc))
761 {
762 LogRel(("Shared Clipboard: Unable to create window, rc=%Rrc\n", rc));
763 return rc;
764 }
765
766 pCtx->fStarted = true; /* Set started indicator. */
767
768 int rc2 = RTThreadUserSignal(hThread);
769 bool fSignalled = RT_SUCCESS(rc2);
770
771 LogRel2(("Shared Clipboard: Window thread running\n"));
772
773 if (RT_SUCCESS(rc))
774 {
775 for (;;)
776 {
777 MSG uMsg;
778 BOOL fRet;
779 while ((fRet = GetMessage(&uMsg, 0, 0, 0)) > 0)
780 {
781 TranslateMessage(&uMsg);
782 DispatchMessage(&uMsg);
783 }
784 Assert(fRet >= 0);
785
786 if (ASMAtomicReadBool(&pCtx->fShutdown))
787 break;
788
789 /** @todo Immediately drop on failure? */
790 }
791 }
792
793 if (!fSignalled)
794 {
795 rc2 = RTThreadUserSignal(hThread);
796 AssertRC(rc2);
797 }
798
799#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
800 OleSetClipboard(NULL); /* Make sure to flush the clipboard on destruction. */
801 OleUninitialize();
802#endif
803
804 LogRel(("Shared Clipboard: Window thread ended\n"));
805
806 LogFlowFuncLeaveRC(rc);
807 return rc;
808}
809
810static void vboxClipboardDestroy(PSHCLCONTEXT pCtx)
811{
812 AssertPtrReturnVoid(pCtx);
813
814 LogFlowFunc(("pCtx=%p\n", pCtx));
815
816 LogRel2(("Shared Clipboard: Destroying ...\n"));
817
818 const PSHCLWINCTX pWinCtx = &pCtx->Win;
819
820 if (pCtx->hThread != NIL_RTTHREAD)
821 {
822 int rcThread = VERR_WRONG_ORDER;
823 int rc = RTThreadWait(pCtx->hThread, 60 * 1000 /* Timeout in ms */, &rcThread);
824 LogFlowFunc(("Waiting for thread resulted in %Rrc (thread exited with %Rrc)\n",
825 rc, rcThread));
826 RT_NOREF(rc);
827 }
828
829 if (pWinCtx->hWnd)
830 {
831 DestroyWindow(pWinCtx->hWnd);
832 pWinCtx->hWnd = NULL;
833 }
834
835 UnregisterClass(s_szClipWndClassName, pCtx->pEnv->hInstance);
836
837 SharedClipboardWinCtxDestroy(&pCtx->Win);
838
839 LogRel2(("Shared Clipboard: Destroyed\n"));
840}
841
842static LRESULT CALLBACK vboxClipboardWinWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
843{
844 PSHCLCONTEXT pCtx = &g_Ctx; /** @todo r=andy Make pCtx available through SetWindowLongPtr() / GWL_USERDATA. */
845 AssertPtr(pCtx);
846
847 /* Forward with proper context. */
848 return vboxClipboardWinProcessMsg(pCtx, hWnd, uMsg, wParam, lParam);
849}
850
851DECLCALLBACK(int) VBoxShClInit(const PVBOXSERVICEENV pEnv, void **ppInstance)
852{
853 LogFlowFuncEnter();
854
855 PSHCLCONTEXT pCtx = &g_Ctx; /* Only one instance for now. */
856 AssertPtr(pCtx);
857
858 if (pCtx->pEnv)
859 {
860 /* Clipboard was already initialized. 2 or more instances are not supported. */
861 return VERR_NOT_SUPPORTED;
862 }
863
864 if (VbglR3AutoLogonIsRemoteSession())
865 {
866 /* Do not use clipboard for remote sessions. */
867 LogRel(("Shared Clipboard: Clipboard has been disabled for a remote session\n"));
868 return VERR_NOT_SUPPORTED;
869 }
870
871 pCtx->pEnv = pEnv;
872 pCtx->hThread = NIL_RTTHREAD;
873 pCtx->fStarted = false;
874 pCtx->fShutdown = false;
875
876 int rc = VINF_SUCCESS;
877
878#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
879 /*
880 * Set callbacks.
881 * Those will be registered within VbglR3 when a new transfer gets initialized.
882 */
883 RT_ZERO(pCtx->CmdCtx.Transfers.Callbacks);
884
885 pCtx->CmdCtx.Transfers.Callbacks.pvUser = pCtx; /* Assign context as user-provided callback data. */
886 pCtx->CmdCtx.Transfers.Callbacks.cbUser = sizeof(SHCLCONTEXT);
887
888 pCtx->CmdCtx.Transfers.Callbacks.pfnOnInitialize = vboxClipboardOnTransferInitCallback;
889 pCtx->CmdCtx.Transfers.Callbacks.pfnOnStart = vboxClipboardOnTransferStartCallback;
890 pCtx->CmdCtx.Transfers.Callbacks.pfnOnCompleted = vboxClipboardOnTransferCompletedCallback;
891 pCtx->CmdCtx.Transfers.Callbacks.pfnOnError = vboxClipboardOnTransferErrorCallback;
892#endif
893
894 if (RT_SUCCESS(rc))
895 {
896 rc = SharedClipboardWinCtxInit(&pCtx->Win);
897 if (RT_SUCCESS(rc))
898 rc = VbglR3ClipboardConnectEx(&pCtx->CmdCtx, VBOX_SHCL_GF_0_CONTEXT_ID);
899 if (RT_SUCCESS(rc))
900 {
901#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
902 rc = ShClTransferCtxInit(&pCtx->TransferCtx);
903#endif
904 if (RT_SUCCESS(rc))
905 {
906 /* Message pump thread for our proxy window. */
907 rc = RTThreadCreate(&pCtx->hThread, vboxClipboardWindowThread, pCtx /* pvUser */,
908 0, RTTHREADTYPE_MSG_PUMP, RTTHREADFLAGS_WAITABLE,
909 "shclwnd");
910 if (RT_SUCCESS(rc))
911 {
912 int rc2 = RTThreadUserWait(pCtx->hThread, 30 * 1000 /* Timeout in ms */);
913 AssertRC(rc2);
914
915 if (!pCtx->fStarted) /* Did the thread fail to start? */
916 rc = VERR_NOT_SUPPORTED; /* Report back Shared Clipboard as not being supported. */
917 }
918 }
919
920 if (RT_SUCCESS(rc))
921 {
922 *ppInstance = pCtx;
923 }
924 else
925 VbglR3ClipboardDisconnectEx(&pCtx->CmdCtx);
926 }
927 }
928
929 if (RT_FAILURE(rc))
930 LogRel(("Shared Clipboard: Unable to initialize, rc=%Rrc\n", rc));
931
932 LogFlowFuncLeaveRC(rc);
933 return rc;
934}
935
936DECLCALLBACK(int) VBoxShClWorker(void *pInstance, bool volatile *pfShutdown)
937{
938 AssertPtr(pInstance);
939 LogFlowFunc(("pInstance=%p\n", pInstance));
940
941 /*
942 * Tell the control thread that it can continue
943 * spawning services.
944 */
945 RTThreadUserSignal(RTThreadSelf());
946
947 const PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pInstance;
948 AssertPtr(pCtx);
949
950 const PSHCLWINCTX pWinCtx = &pCtx->Win;
951
952 LogRel2(("Shared Clipboard: Worker loop running\n"));
953
954#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
955 HRESULT hr = OleInitialize(NULL);
956 if (FAILED(hr))
957 {
958 LogRel(("Shared Clipboard: Initializing OLE in worker thread failed (%Rhrc) -- file transfers unavailable\n", hr));
959 /* Not critical, the rest of the clipboard might work. */
960 }
961 else
962 LogRel(("Shared Clipboard: Initialized OLE in worker thraed\n"));
963#endif
964
965 int rc;
966
967 /* The thread waits for incoming messages from the host. */
968 for (;;)
969 {
970 LogFlowFunc(("Waiting for host message (fUseLegacyProtocol=%RTbool, fHostFeatures=%#RX64) ...\n",
971 pCtx->CmdCtx.fUseLegacyProtocol, pCtx->CmdCtx.fHostFeatures));
972
973 PVBGLR3CLIPBOARDEVENT pEvent = (PVBGLR3CLIPBOARDEVENT)RTMemAllocZ(sizeof(VBGLR3CLIPBOARDEVENT));
974 AssertPtrBreakStmt(pEvent, rc = VERR_NO_MEMORY);
975
976 uint32_t idMsg = 0;
977 uint32_t cParms = 0;
978 rc = VbglR3ClipboardMsgPeekWait(&pCtx->CmdCtx, &idMsg, &cParms, NULL /* pidRestoreCheck */);
979 if (RT_SUCCESS(rc))
980 {
981#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
982 rc = VbglR3ClipboardEventGetNextEx(idMsg, cParms, &pCtx->CmdCtx, &pCtx->TransferCtx, pEvent);
983#else
984 rc = VbglR3ClipboardEventGetNext(idMsg, cParms, &pCtx->CmdCtx, pEvent);
985#endif
986 }
987
988 if (RT_FAILURE(rc))
989 {
990 LogFlowFunc(("Getting next event failed with %Rrc\n", rc));
991
992 VbglR3ClipboardEventFree(pEvent);
993 pEvent = NULL;
994
995 if (*pfShutdown)
996 break;
997
998 /* Wait a bit before retrying. */
999 RTThreadSleep(1000);
1000 continue;
1001 }
1002 else
1003 {
1004 AssertPtr(pEvent);
1005 LogFlowFunc(("Event uType=%RU32\n", pEvent->enmType));
1006
1007 switch (pEvent->enmType)
1008 {
1009 case VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS:
1010 {
1011 /* The host has announced available clipboard formats.
1012 * Forward the information to the window, so it can later
1013 * respond to WM_RENDERFORMAT message. */
1014 ::PostMessage(pWinCtx->hWnd, SHCL_WIN_WM_REPORT_FORMATS,
1015 0 /* wParam */, (LPARAM)pEvent /* lParam */);
1016
1017 pEvent = NULL; /* Consume pointer. */
1018 break;
1019 }
1020
1021 case VBGLR3CLIPBOARDEVENTTYPE_READ_DATA:
1022 {
1023 /* The host needs data in the specified format. */
1024 ::PostMessage(pWinCtx->hWnd, SHCL_WIN_WM_READ_DATA,
1025 0 /* wParam */, (LPARAM)pEvent /* lParam */);
1026
1027 pEvent = NULL; /* Consume pointer. */
1028 break;
1029 }
1030
1031 case VBGLR3CLIPBOARDEVENTTYPE_QUIT:
1032 {
1033 LogRel2(("Shared Clipboard: Host requested termination\n"));
1034 ASMAtomicXchgBool(pfShutdown, true);
1035 break;
1036 }
1037
1038#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1039 case VBGLR3CLIPBOARDEVENTTYPE_TRANSFER_STATUS:
1040 {
1041 /* Nothing to do here. */
1042 rc = VINF_SUCCESS;
1043 break;
1044 }
1045#endif
1046 case VBGLR3CLIPBOARDEVENTTYPE_NONE:
1047 {
1048 /* Nothing to do here. */
1049 rc = VINF_SUCCESS;
1050 break;
1051 }
1052
1053 default:
1054 {
1055 AssertMsgFailedBreakStmt(("Event type %RU32 not implemented\n", pEvent->enmType), rc = VERR_NOT_SUPPORTED);
1056 }
1057 }
1058
1059 if (pEvent)
1060 {
1061 VbglR3ClipboardEventFree(pEvent);
1062 pEvent = NULL;
1063 }
1064 }
1065
1066 if (*pfShutdown)
1067 break;
1068 }
1069
1070 LogRel2(("Shared Clipboard: Worker loop ended\n"));
1071
1072#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1073 OleSetClipboard(NULL); /* Make sure to flush the clipboard on destruction. */
1074 OleUninitialize();
1075#endif
1076
1077 LogFlowFuncLeaveRC(rc);
1078 return rc;
1079}
1080
1081DECLCALLBACK(int) VBoxShClStop(void *pInstance)
1082{
1083 AssertPtrReturn(pInstance, VERR_INVALID_POINTER);
1084
1085 LogFunc(("Stopping pInstance=%p\n", pInstance));
1086
1087 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pInstance;
1088 AssertPtr(pCtx);
1089
1090 /* Set shutdown indicator. */
1091 ASMAtomicWriteBool(&pCtx->fShutdown, true);
1092
1093 /* Let our clipboard know that we're going to shut down. */
1094 PostMessage(pCtx->Win.hWnd, WM_QUIT, 0, 0);
1095
1096 /* Disconnect from the host service.
1097 * This will also send a VBOX_SHCL_HOST_MSG_QUIT from the host so that we can break out from our message worker. */
1098 VbglR3ClipboardDisconnect(pCtx->CmdCtx.idClient);
1099 pCtx->CmdCtx.idClient = 0;
1100
1101 LogFlowFuncLeaveRC(VINF_SUCCESS);
1102 return VINF_SUCCESS;
1103}
1104
1105DECLCALLBACK(void) VBoxShClDestroy(void *pInstance)
1106{
1107 AssertPtrReturnVoid(pInstance);
1108
1109 PSHCLCONTEXT pCtx = (PSHCLCONTEXT)pInstance;
1110 AssertPtr(pCtx);
1111
1112 /* Make sure that we are disconnected. */
1113 Assert(pCtx->CmdCtx.idClient == 0);
1114
1115 vboxClipboardDestroy(pCtx);
1116
1117#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1118 ShClTransferCtxDestroy(&pCtx->TransferCtx);
1119#endif
1120
1121 return;
1122}
1123
1124/**
1125 * The service description.
1126 */
1127VBOXSERVICEDESC g_SvcDescClipboard =
1128{
1129 /* pszName. */
1130 "clipboard",
1131 /* pszDescription. */
1132 "Shared Clipboard",
1133 /* methods */
1134 VBoxShClInit,
1135 VBoxShClWorker,
1136 VBoxShClStop,
1137 VBoxShClDestroy
1138};
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