VirtualBox

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

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

VBoxTray,VBoxClient: Use '&' rather than '==' when testing format requested in VBOX_SHCL_HOST_MSG_READ_DATA, as darwin hosts may (incorrectly) request more than one format. bugref:9437

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