VirtualBox

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

Last change on this file since 82472 was 82462, checked in by vboxsync, 5 years ago

Shared Clipboard/Transfers: Renamed SHCLTRANSFERDIR_[READ|WRITE] -> SHCLTRANSFERDIR_[FROM|TO]_REMOTE.

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

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