VirtualBox

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

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

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