VirtualBox

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

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

Shared Clipboard/Transfers: Resurrected Windows code. bugref:9437

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