VirtualBox

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

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

*: doxygen fixes

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