VirtualBox

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

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

VBoxTray/VBoxClipboard: Drop strsafe.h as it requires stdio.h which we don't have in VBOX_WITH_NOCRT_STATIC mode. Replacing StringCbLengthW with RTUtf16NLenEx. bugref:10261

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