VirtualBox

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

Last change on this file since 98126 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

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