VirtualBox

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

最後變更 在這個檔案從95837是 93396,由 vboxsync 提交於 3 年 前

VBoxTray/Clipboard: If SharedClipboardWinConvertCFHTMLToMIME fails, just write the raw text to the host so the host doesn't end up waiting on data that won't ever arrive. bugref:10160

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette