VirtualBox

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

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

iprt/cdefs.h: Refactored the typedef use of DECLCALLBACK as well as DECLCALLBACKMEMBER to wrap the whole expression, similar to the DECLR?CALLBACKMEMBER macros. This allows adding a throw() at the end when compiling with the VC++ compiler to indicate that the callbacks won't throw anything, so we can stop supressing the C5039 warning about passing functions that can potential throw C++ exceptions to extern C code that can't necessarily cope with such (unwind,++). Introduced a few _EX variations that allows specifying different/no calling convention too, as that's handy when dynamically resolving host APIs. Fixed numerous places missing DECLCALLBACK and such. Left two angry @todos regarding use of CreateThread. bugref:9794

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