VirtualBox

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

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

Shared Clipboard/URI: Also use the importchecker if VBOX_WITH_SHARED_CLIPBOARD_URI_LIST is set with VBoxTray now.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.2 KB
Line 
1/* $Id: VBoxClipboard.cpp 80324 2019-08-16 09:40:21Z vboxsync $ */
2/** @file
3 * VBoxClipboard - Shared clipboard, Windows Guest Implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
23#include <VBox/log.h>
24
25#include "VBoxTray.h"
26#include "VBoxHelpers.h"
27
28#include <iprt/asm.h>
29#include <iprt/ldr.h>
30
31#include <iprt/errcore.h>
32
33#include <VBox/GuestHost/SharedClipboard.h>
34#include <VBox/HostServices/VBoxClipboardSvc.h> /* Temp, remove. */
35#include <VBox/GuestHost/SharedClipboard-win.h>
36#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
37# include <VBox/GuestHost/SharedClipboard-uri.h>
38#endif
39
40#include <strsafe.h>
41
42#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
43# include <iprt/win/shlobj.h>
44# include <iprt/win/shlwapi.h>
45#endif
46
47
48/*********************************************************************************************************************************
49* Structures and Typedefs *
50*********************************************************************************************************************************/
51
52typedef struct _VBOXCLIPBOARDCONTEXT
53{
54 /** Pointer to the VBoxClient service environment. */
55 const VBOXSERVICEENV *pEnv;
56 /** Command context. */
57 VBGLR3SHCLCMDCTX CmdCtx;
58 /** Windows-specific context data. */
59 VBOXCLIPBOARDWINCTX Win;
60#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
61 /** URI transfer data. */
62 SHAREDCLIPBOARDURICTX URI;
63#endif
64} VBOXCLIPBOARDCONTEXT, *PVBOXCLIPBOARDCONTEXT;
65
66#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
67typedef struct _VBOXCLIPBOARDURIREADTHREADCTX
68{
69 PVBOXCLIPBOARDCONTEXT pClipboardCtx;
70 PSHAREDCLIPBOARDURITRANSFER pTransfer;
71} VBOXCLIPBOARDURIREADTHREADCTX, *PVBOXCLIPBOARDURIREADTHREADCTX;
72
73typedef struct _VBOXCLIPBOARDURIWRITETHREADCTX
74{
75 PVBOXCLIPBOARDCONTEXT pClipboardCtx;
76 PSHAREDCLIPBOARDURITRANSFER pTransfer;
77} VBOXCLIPBOARDURIWRITETHREADCTX, *PVBOXCLIPBOARDURIWRITETHREADCTX;
78#endif /* VBOX_WITH_SHARED_CLIPBOARD_URI_LIST */
79
80
81/*********************************************************************************************************************************
82* Static variables *
83*********************************************************************************************************************************/
84/** Static clipboard context (since it is the single instance). Directly used in the windows proc. */
85static VBOXCLIPBOARDCONTEXT g_Ctx = { NULL };
86/** Static window class name. */
87static char s_szClipWndClassName[] = VBOX_CLIPBOARD_WNDCLASS_NAME;
88
89
90/*********************************************************************************************************************************
91* Prototypes *
92*********************************************************************************************************************************/
93#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
94static DECLCALLBACK(void) vboxClipboardURITransferCompleteCallback(PSHAREDCLIPBOARDURITRANSFERCALLBACKDATA pData, int rc);
95static DECLCALLBACK(void) vboxClipboardURITransferErrorCallback(PSHAREDCLIPBOARDURITRANSFERCALLBACKDATA pData, int rc);
96#endif
97
98
99#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
100static DECLCALLBACK(int) vboxClipboardURIWriteThread(RTTHREAD ThreadSelf, void *pvUser)
101{
102 RT_NOREF(ThreadSelf);
103
104 LogFlowFuncEnter();
105
106 PVBOXCLIPBOARDURIWRITETHREADCTX pCtx = (PVBOXCLIPBOARDURIWRITETHREADCTX)pvUser;
107 AssertPtr(pCtx);
108
109 PSHAREDCLIPBOARDURITRANSFER pTransfer = pCtx->pTransfer;
110 AssertPtr(pTransfer);
111
112 pTransfer->Thread.fStarted = true;
113
114 RTThreadUserSignal(RTThreadSelf());
115
116 VBGLR3SHCLCMDCTX cmdCtx;
117 RT_ZERO(cmdCtx);
118
119 int rc = VbglR3ClipboardConnect(&cmdCtx.uClientID);
120 if (RT_SUCCESS(rc))
121 {
122 rc = VbglR3ClipboardTransferSendStatus(&cmdCtx, SHAREDCLIPBOARDURITRANSFERSTATUS_RUNNING);
123 if (RT_SUCCESS(rc))
124 {
125 bool fTerminate = false;
126 unsigned cErrors = 0;
127
128 for (;;)
129 {
130 PVBGLR3CLIPBOARDEVENT pEvent = NULL;
131 rc = VbglR3ClipboardEventGetNext(&cmdCtx, pTransfer, &pEvent);
132 if (RT_SUCCESS(rc))
133 {
134 /* Nothing to do in here right now. */
135
136 VbglR3ClipboardEventFree(pEvent);
137 }
138
139 if (fTerminate)
140 break;
141
142 if (RT_FAILURE(rc))
143 {
144 if (cErrors++ >= 3)
145 break;
146 RTThreadSleep(1000);
147 }
148 }
149 }
150
151 VbglR3ClipboardDisconnect(cmdCtx.uClientID);
152 }
153
154 RTMemFree(pCtx);
155
156 LogFlowFuncLeaveRC(rc);
157 return rc;
158}
159
160static DECLCALLBACK(void) vboxClipboardURITransferCompleteCallback(PSHAREDCLIPBOARDURITRANSFERCALLBACKDATA pData, int rc)
161{
162 RT_NOREF(rc);
163
164 LogFlowFunc(("pData=%p, rc=%Rrc\n", pData, rc));
165
166 LogRel2(("Shared Clipboard: Transfer to destination complete\n"));
167
168 PSHAREDCLIPBOARDURICTX pCtx = (PSHAREDCLIPBOARDURICTX)pData->pvUser;
169 AssertPtr(pCtx);
170
171 PSHAREDCLIPBOARDURITRANSFER pTransfer = pData->pTransfer;
172 AssertPtr(pTransfer);
173
174 if (pTransfer->pvUser) /* SharedClipboardWinURITransferCtx */
175 {
176 delete pTransfer->pvUser;
177 pTransfer->pvUser = NULL;
178 }
179
180 int rc2 = SharedClipboardURICtxTransferRemove(pCtx, pTransfer);
181 AssertRC(rc2);
182}
183
184static DECLCALLBACK(void) vboxClipboardURITransferErrorCallback(PSHAREDCLIPBOARDURITRANSFERCALLBACKDATA pData, int rc)
185{
186 RT_NOREF(rc);
187
188 LogFlowFunc(("pData=%p, rc=%Rrc\n", pData, rc));
189
190 LogRel(("Shared Clipboard: Transfer to destination failed with %Rrc\n", rc));
191
192 PSHAREDCLIPBOARDURICTX pCtx = (PSHAREDCLIPBOARDURICTX)pData->pvUser;
193 AssertPtr(pCtx);
194
195 PSHAREDCLIPBOARDURITRANSFER pTransfer = pData->pTransfer;
196 AssertPtr(pTransfer);
197
198 if (pTransfer->pvUser) /* SharedClipboardWinURITransferCtx */
199 {
200 delete pTransfer->pvUser;
201 pTransfer->pvUser = NULL;
202 }
203
204 int rc2 = SharedClipboardURICtxTransferRemove(pCtx, pTransfer);
205 AssertRC(rc2);
206}
207
208static int vboxClipboardURITransferOpen(PSHAREDCLIPBOARDPROVIDERCTX pCtx)
209{
210 RT_NOREF(pCtx);
211
212 LogFlowFuncLeave();
213 return VINF_SUCCESS;
214}
215
216static int vboxClipboardURITransferClose(PSHAREDCLIPBOARDPROVIDERCTX pCtx)
217{
218 RT_NOREF(pCtx);
219
220 LogFlowFuncLeave();
221 return VINF_SUCCESS;
222}
223
224static int vboxClipboardURIListOpen(PSHAREDCLIPBOARDPROVIDERCTX pCtx, PVBOXCLIPBOARDLISTHDR pListHdr,
225 PSHAREDCLIPBOARDLISTHANDLE phList)
226{
227 RT_NOREF(pCtx, pListHdr, phList);
228
229 LogFlowFuncEnter();
230
231 PVBOXCLIPBOARDCONTEXT pThisCtx = (PVBOXCLIPBOARDCONTEXT)pCtx->pvUser;
232 AssertPtr(pThisCtx);
233
234 RT_NOREF(pThisCtx);
235
236 int rc = 0; // VbglR3ClipboardRecvListOpen(pThisCtx->u32ClientID, pListHdr, phList);
237
238 LogFlowFuncLeaveRC(rc);
239 return rc;
240}
241
242static int vboxClipboardURIListHdrRead(PSHAREDCLIPBOARDPROVIDERCTX pCtx, SHAREDCLIPBOARDLISTHANDLE hList,
243 PVBOXCLIPBOARDLISTHDR pListHdr)
244{
245 RT_NOREF(hList);
246
247 LogFlowFuncEnter();
248
249 PVBOXCLIPBOARDCONTEXT pThisCtx = (PVBOXCLIPBOARDCONTEXT)pCtx->pvUser;
250 AssertPtr(pThisCtx);
251
252 RT_NOREF(pThisCtx);
253
254 int rc = SharedClipboardURIListHdrInit(pListHdr);
255 if (RT_SUCCESS(rc))
256 {
257 if (RT_SUCCESS(rc))
258 {
259 //rc = VbglR3ClipboardListHdrReadRecv(pThisCtx->u32ClientID, hList, pListHdr);
260 }
261 else
262 SharedClipboardURIListHdrDestroy(pListHdr);
263 }
264
265 LogFlowFuncLeaveRC(rc);
266 return rc;
267}
268
269/*
270static int vboxClipboardURIListHdrWrite(PSHAREDCLIPBOARDPROVIDERCTX pCtx, SHAREDCLIPBOARDLISTHANDLE hList,
271 PVBOXCLIPBOARDLISTHDR pListHdr)
272{
273 LogFlowFuncEnter();
274
275 PVBOXCLIPBOARDCONTEXT pThisCtx = (PVBOXCLIPBOARDCONTEXT)pCtx->pvUser;
276 AssertPtr(pThisCtx);
277
278 int rc = VbglR3ClipboardListHdrWrite(pThisCtx->u32ClientID, hList, pListHdr);
279
280 LogFlowFuncLeaveRC(rc);
281 return rc;
282}*/
283
284static int vboxClipboardURIListEntryRead(PSHAREDCLIPBOARDPROVIDERCTX pCtx, SHAREDCLIPBOARDLISTHANDLE hList,
285 PVBOXCLIPBOARDLISTENTRY pListEntry)
286{
287 RT_NOREF(hList);
288
289 LogFlowFuncEnter();
290
291 PVBOXCLIPBOARDCONTEXT pThisCtx = (PVBOXCLIPBOARDCONTEXT)pCtx->pvUser;
292 AssertPtr(pThisCtx);
293
294 RT_NOREF(pThisCtx);
295
296 RT_NOREF(pListEntry);
297 int rc = 0; // VbglR3ClipboardListEntryRead(pThisCtx->u32ClientID, pListEntry);
298
299 LogFlowFuncLeaveRC(rc);
300 return rc;
301}
302
303/*
304static int vboxClipboardURIListEntryWrite(PSHAREDCLIPBOARDPROVIDERCTX pCtx, SHAREDCLIPBOARDLISTHANDLE hList,
305 PVBOXCLIPBOARDLISTENTRY pListEntry)
306{
307 LogFlowFuncEnter();
308
309 PVBOXCLIPBOARDCONTEXT pThisCtx = (PVBOXCLIPBOARDCONTEXT)pCtx->pvUser;
310 AssertPtr(pThisCtx);
311
312 int rc = VbglR3ClipboardListEntryWrite(pThisCtx->u32ClientID, hList, pListEntry);
313
314 LogFlowFuncLeaveRC(rc);
315 return rc;
316}
317*/
318
319static int vboxClipboardURIObjOpen(PSHAREDCLIPBOARDPROVIDERCTX pCtx,
320 PVBOXCLIPBOARDOBJOPENCREATEPARMS pCreateParms, PSHAREDCLIPBOARDOBJHANDLE phObj)
321{
322 LogFlowFuncEnter();
323
324 PVBOXCLIPBOARDCONTEXT pThisCtx = (PVBOXCLIPBOARDCONTEXT)pCtx->pvUser;
325 AssertPtr(pThisCtx);
326
327 int rc = VbglR3ClipboardObjOpenSend(&pThisCtx->CmdCtx, pCreateParms, phObj);
328
329 LogFlowFuncLeaveRC(rc);
330 return rc;
331}
332
333static int vboxClipboardURIObjClose(PSHAREDCLIPBOARDPROVIDERCTX pCtx, SHAREDCLIPBOARDOBJHANDLE hObj)
334{
335 LogFlowFuncEnter();
336
337 PVBOXCLIPBOARDCONTEXT pThisCtx = (PVBOXCLIPBOARDCONTEXT)pCtx->pvUser;
338 AssertPtr(pThisCtx);
339
340 int rc = VbglR3ClipboardObjCloseSend(&pThisCtx->CmdCtx, hObj);
341
342 LogFlowFuncLeaveRC(rc);
343 return rc;
344}
345
346static int vboxClipboardURIObjRead(PSHAREDCLIPBOARDPROVIDERCTX pCtx, SHAREDCLIPBOARDOBJHANDLE hObj,
347 void *pvData, uint32_t cbData, uint32_t fFlags, uint32_t *pcbRead)
348{
349 RT_NOREF(fFlags);
350
351 LogFlowFuncEnter();
352
353 PVBOXCLIPBOARDCONTEXT pThisCtx = (PVBOXCLIPBOARDCONTEXT)pCtx->pvUser;
354 AssertPtr(pThisCtx);
355
356 int rc = VbglR3ClipboardObjReadSend(&pThisCtx->CmdCtx, hObj, pvData, cbData, pcbRead);
357
358 LogFlowFuncLeaveRC(rc);
359 return rc;
360}
361
362static int vboxClipboardURIObjWrite(PSHAREDCLIPBOARDPROVIDERCTX pCtx, SHAREDCLIPBOARDOBJHANDLE hObj,
363 void *pvData, uint32_t cbData, uint32_t fFlags, uint32_t *pcbWritten)
364{
365 RT_NOREF(fFlags);
366
367 LogFlowFuncEnter();
368
369 PVBOXCLIPBOARDCONTEXT pThisCtx = (PVBOXCLIPBOARDCONTEXT)pCtx->pvUser;
370 AssertPtr(pThisCtx);
371
372 int rc = VbglR3ClipboardObjWriteSend(&pThisCtx->CmdCtx, hObj, pvData, cbData, pcbWritten);
373
374 LogFlowFuncLeaveRC(rc);
375 return rc;
376}
377#endif /* VBOX_WITH_SHARED_CLIPBOARD_URI_LIST */
378
379static LRESULT vboxClipboardWinProcessMsg(PVBOXCLIPBOARDCONTEXT pCtx, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
380{
381 AssertPtr(pCtx);
382
383 const PVBOXCLIPBOARDWINCTX pWinCtx = &pCtx->Win;
384
385 LRESULT lresultRc = 0;
386
387 switch (msg)
388 {
389 case WM_CLIPBOARDUPDATE:
390 {
391 const HWND hWndClipboardOwner = GetClipboardOwner();
392 if (pWinCtx->hWndClipboardOwnerUs != hWndClipboardOwner)
393 {
394 LogFunc(("WM_CLIPBOARDUPDATE: hWndOldClipboardOwner=%p, hWndNewClipboardOwner=%p\n",
395 pWinCtx->hWndClipboardOwnerUs, hWndClipboardOwner));
396
397 /* Clipboard was updated by another application.
398 * Report available formats to the host. */
399 VBOXCLIPBOARDFORMATS fFormats;
400 int rc = VBoxClipboardWinGetFormats(&pCtx->Win, &fFormats);
401 if (RT_SUCCESS(rc))
402 {
403 LogFunc(("WM_CLIPBOARDUPDATE: Reporting formats 0x%x\n", fFormats));
404 rc = VbglR3ClipboardReportFormats(pCtx->CmdCtx.uClientID, fFormats);
405 }
406 }
407 }
408 break;
409
410 case WM_CHANGECBCHAIN:
411 {
412 LogFunc(("WM_CHANGECBCHAIN\n"));
413 lresultRc = VBoxClipboardWinHandleWMChangeCBChain(pWinCtx, hwnd, msg, wParam, lParam);
414 }
415 break;
416
417 case WM_DRAWCLIPBOARD:
418 {
419 LogFlowFunc(("WM_DRAWCLIPBOARD, hwnd %p\n", pWinCtx->hWnd));
420
421 if (GetClipboardOwner() != hwnd)
422 {
423 /* Clipboard was updated by another application. */
424 /* WM_DRAWCLIPBOARD always expects a return code of 0, so don't change "rc" here. */
425 VBOXCLIPBOARDFORMATS fFormats;
426 int rc = VBoxClipboardWinGetFormats(pWinCtx, &fFormats);
427 if (RT_SUCCESS(rc))
428 rc = VbglR3ClipboardReportFormats(pCtx->CmdCtx.uClientID, fFormats);
429 }
430
431 lresultRc = VBoxClipboardWinChainPassToNext(pWinCtx, msg, wParam, lParam);
432 }
433 break;
434
435 case WM_TIMER:
436 {
437 int rc = VBoxClipboardWinHandleWMTimer(pWinCtx);
438 AssertRC(rc);
439 }
440 break;
441
442 case WM_CLOSE:
443 {
444 /* Do nothing. Ignore the message. */
445 }
446 break;
447
448 case WM_RENDERFORMAT:
449 {
450 LogFunc(("WM_RENDERFORMAT\n"));
451
452 /* Insert the requested clipboard format data into the clipboard. */
453 const UINT cfFormat = (UINT)wParam;
454
455 const VBOXCLIPBOARDFORMAT fFormat = VBoxClipboardWinClipboardFormatToVBox(cfFormat);
456
457 LogFunc(("WM_RENDERFORMAT: cfFormat=%u -> fFormat=0x%x\n", cfFormat, fFormat));
458
459 if (fFormat == VBOX_SHARED_CLIPBOARD_FMT_NONE)
460 {
461 LogFunc(("WM_RENDERFORMAT: Unsupported format requested\n"));
462 VBoxClipboardWinClear();
463 }
464 else
465 {
466 const uint32_t cbPrealloc = _4K;
467 uint32_t cb = 0;
468
469 /* Preallocate a buffer, most of small text transfers will fit into it. */
470 HANDLE hMem = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, cbPrealloc);
471 LogFlowFunc(("Preallocated handle hMem = %p\n", hMem));
472
473 if (hMem)
474 {
475 void *pMem = GlobalLock(hMem);
476 LogFlowFunc(("Locked pMem = %p, GlobalSize = %ld\n", pMem, GlobalSize(hMem)));
477
478 if (pMem)
479 {
480 /* Read the host data to the preallocated buffer. */
481 int rc = VbglR3ClipboardReadData(pCtx->CmdCtx.uClientID, fFormat, pMem, cbPrealloc, &cb);
482 LogFlowFunc(("VbglR3ClipboardReadData returned with rc = %Rrc\n", rc));
483
484 if (RT_SUCCESS(rc))
485 {
486 if (cb == 0)
487 {
488 /* 0 bytes returned means the clipboard is empty.
489 * Deallocate the memory and set hMem to NULL to get to
490 * the clipboard empty code path. */
491 GlobalUnlock(hMem);
492 GlobalFree(hMem);
493 hMem = NULL;
494 }
495 else if (cb > cbPrealloc)
496 {
497 GlobalUnlock(hMem);
498
499 /* The preallocated buffer is too small, adjust the size. */
500 hMem = GlobalReAlloc(hMem, cb, 0);
501 LogFlowFunc(("Reallocated hMem = %p\n", hMem));
502
503 if (hMem)
504 {
505 pMem = GlobalLock(hMem);
506 LogFlowFunc(("Locked pMem = %p, GlobalSize = %ld\n", pMem, GlobalSize(hMem)));
507
508 if (pMem)
509 {
510 /* Read the host data to the preallocated buffer. */
511 uint32_t cbNew = 0;
512 rc = VbglR3ClipboardReadData(pCtx->CmdCtx.uClientID, fFormat, pMem, cb, &cbNew);
513 LogFlowFunc(("VbglR3ClipboardReadData returned with rc = %Rrc, cb = %d, cbNew = %d\n",
514 rc, cb, cbNew));
515
516 if (RT_SUCCESS(rc)
517 && cbNew <= cb)
518 {
519 cb = cbNew;
520 }
521 else
522 {
523 GlobalUnlock(hMem);
524 GlobalFree(hMem);
525 hMem = NULL;
526 }
527 }
528 else
529 {
530 GlobalFree(hMem);
531 hMem = NULL;
532 }
533 }
534 }
535
536 if (hMem)
537 {
538 /* pMem is the address of the data. cb is the size of returned data. */
539 /* Verify the size of returned text, the memory block for clipboard
540 * must have the exact string size.
541 */
542 if (fFormat == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
543 {
544 size_t cbActual = 0;
545 HRESULT hrc = StringCbLengthW((LPWSTR)pMem, cb, &cbActual);
546 if (FAILED(hrc))
547 {
548 /* Discard invalid data. */
549 GlobalUnlock(hMem);
550 GlobalFree(hMem);
551 hMem = NULL;
552 }
553 else
554 {
555 /* cbActual is the number of bytes, excluding those used
556 * for the terminating null character.
557 */
558 cb = (uint32_t)(cbActual + 2);
559 }
560 }
561 }
562
563 if (hMem)
564 {
565 GlobalUnlock(hMem);
566
567 hMem = GlobalReAlloc(hMem, cb, 0);
568 LogFlowFunc(("Reallocated hMem = %p\n", hMem));
569
570 if (hMem)
571 {
572 /* 'hMem' contains the host clipboard data.
573 * size is 'cb' and format is 'format'. */
574 HANDLE hClip = SetClipboardData(cfFormat, hMem);
575 LogFlowFunc(("WM_RENDERFORMAT hClip = %p\n", hClip));
576
577 if (hClip)
578 {
579 /* The hMem ownership has gone to the system. Finish the processing. */
580 break;
581 }
582
583 /* Cleanup follows. */
584 }
585 }
586 }
587 if (hMem)
588 GlobalUnlock(hMem);
589 }
590 if (hMem)
591 GlobalFree(hMem);
592 }
593 }
594 }
595 break;
596
597 case WM_RENDERALLFORMATS:
598 {
599 LogFunc(("WM_RENDERALLFORMATS\n"));
600
601 int rc = VBoxClipboardWinHandleWMRenderAllFormats(pWinCtx, hwnd);
602 AssertRC(rc);
603 }
604 break;
605
606 case VBOX_CLIPBOARD_WM_SET_FORMATS:
607 {
608 LogFunc(("VBOX_CLIPBOARD_WM_SET_FORMATS\n"));
609
610 /* Announce available formats. Do not insert data -- will be inserted in WM_RENDERFORMAT. */
611 VBOXCLIPBOARDFORMATS fFormats = (uint32_t)lParam;
612 if (fFormats != VBOX_SHARED_CLIPBOARD_FMT_NONE) /* Could arrive with some older GA versions. */
613 {
614 int rc = VBoxClipboardWinOpen(hwnd);
615 if (RT_SUCCESS(rc))
616 {
617 VBoxClipboardWinClear();
618
619#if 0
620 if (fFormats & VBOX_SHARED_CLIPBOARD_FMT_URI_LIST)
621 {
622 LogFunc(("VBOX_SHARED_CLIPBOARD_FMT_URI_LIST\n"));
623
624 PSHAREDCLIPBOARDURITRANSFER pTransfer;
625 rc = SharedClipboardURITransferCreate(SHAREDCLIPBOARDURITRANSFERDIR_READ,
626 SHAREDCLIPBOARDSOURCE_REMOTE,
627 &pTransfer);
628 if (RT_SUCCESS(rc))
629 {
630 SHAREDCLIPBOARDURITRANSFERCALLBACKS TransferCallbacks;
631 RT_ZERO(TransferCallbacks);
632
633 TransferCallbacks.pvUser = &pCtx->URI;
634 TransferCallbacks.pfnTransferComplete = vboxClipboardURITransferCompleteCallback;
635 TransferCallbacks.pfnTransferError = vboxClipboardURITransferErrorCallback;
636
637 SharedClipboardURITransferSetCallbacks(pTransfer, &TransferCallbacks);
638
639 SHAREDCLIPBOARDPROVIDERCREATIONCTX creationCtx;
640 RT_ZERO(creationCtx);
641 creationCtx.enmSource = SHAREDCLIPBOARDSOURCE_REMOTE;
642
643 RT_ZERO(creationCtx.Interface);
644 creationCtx.Interface.pfnTransferOpen = vboxClipboardURITransferOpen;
645 creationCtx.Interface.pfnTransferClose = vboxClipboardURITransferClose;
646 creationCtx.Interface.pfnListHdrRead = vboxClipboardURIListHdrRead;
647 creationCtx.Interface.pfnListEntryRead = vboxClipboardURIListEntryRead;
648 creationCtx.Interface.pfnObjOpen = vboxClipboardURIObjOpen;
649 creationCtx.Interface.pfnObjClose = vboxClipboardURIObjClose;
650 creationCtx.Interface.pfnObjRead = vboxClipboardURIObjRead;
651
652 creationCtx.pvUser = pCtx;
653
654 rc = SharedClipboardURITransferSetInterface(pTransfer, &creationCtx);
655 if (RT_SUCCESS(rc))
656 {
657 rc = SharedClipboardURICtxTransferAdd(&pCtx->URI, pTransfer);
658 if (RT_SUCCESS(rc))
659 rc = VBoxClipboardWinURITransferCreate(pWinCtx, pTransfer);
660 }
661
662 /* Note: VBoxClipboardWinURITransferCreate() takes care of closing the clipboard. */
663
664 LogFunc(("VBOX_SHARED_CLIPBOARD_FMT_URI_LIST: rc=%Rrc\n", rc));
665 }
666 }
667 else
668 {
669#endif
670 rc = VBoxClipboardWinAnnounceFormats(pWinCtx, fFormats);
671
672 VBoxClipboardWinClose();
673#if 0
674 }
675#endif
676 }
677 }
678 LogFunc(("VBOX_CLIPBOARD_WM_SET_FORMATS: fFormats=0x%x, lastErr=%ld\n", fFormats, GetLastError()));
679 }
680 break;
681
682 case VBOX_CLIPBOARD_WM_READ_DATA:
683 {
684 /* Send data in the specified format to the host. */
685 VBOXCLIPBOARDFORMAT uFormat = (uint32_t)lParam;
686 HANDLE hClip = NULL;
687
688 LogFlowFunc(("VBOX_WM_SHCLPB_READ_DATA: uFormat=0x%x\n", uFormat));
689
690 int rc = VBoxClipboardWinOpen(hwnd);
691 if (RT_SUCCESS(rc))
692 {
693 if (uFormat == VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
694 {
695 hClip = GetClipboardData(CF_DIB);
696 if (hClip != NULL)
697 {
698 LPVOID lp = GlobalLock(hClip);
699 if (lp != NULL)
700 {
701 rc = VbglR3ClipboardWriteData(pCtx->CmdCtx.uClientID, VBOX_SHARED_CLIPBOARD_FMT_BITMAP,
702 lp, GlobalSize(hClip));
703 GlobalUnlock(hClip);
704 }
705 else
706 {
707 hClip = NULL;
708 }
709 }
710 }
711 else if (uFormat == VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
712 {
713 hClip = GetClipboardData(CF_UNICODETEXT);
714 if (hClip != NULL)
715 {
716 LPWSTR uniString = (LPWSTR)GlobalLock(hClip);
717 if (uniString != NULL)
718 {
719 rc = VbglR3ClipboardWriteData(pCtx->CmdCtx.uClientID, VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT,
720 uniString, (lstrlenW(uniString) + 1) * 2);
721 GlobalUnlock(hClip);
722 }
723 else
724 {
725 hClip = NULL;
726 }
727 }
728 }
729 else if (uFormat == VBOX_SHARED_CLIPBOARD_FMT_HTML)
730 {
731 UINT format = RegisterClipboardFormat(VBOX_CLIPBOARD_WIN_REGFMT_HTML);
732 if (format != 0)
733 {
734 hClip = GetClipboardData(format);
735 if (hClip != NULL)
736 {
737 LPVOID lp = GlobalLock(hClip);
738
739 if (lp != NULL)
740 {
741 rc = VbglR3ClipboardWriteData(pCtx->CmdCtx.uClientID, VBOX_SHARED_CLIPBOARD_FMT_HTML,
742 lp, GlobalSize(hClip));
743 GlobalUnlock(hClip);
744 }
745 else
746 {
747 hClip = NULL;
748 }
749 }
750 }
751 }
752
753 if (hClip == NULL)
754 {
755 LogFunc(("VBOX_WM_SHCLPB_READ_DATA: hClip=NULL, lastError=%ld\n", GetLastError()));
756
757 /* Requested clipboard format is not available, send empty data. */
758 VbglR3ClipboardWriteData(pCtx->CmdCtx.uClientID, VBOX_SHARED_CLIPBOARD_FMT_NONE, NULL, 0);
759#ifdef DEBUG_andy
760 AssertFailed();
761#endif
762 }
763
764 VBoxClipboardWinClose();
765 }
766 break;
767 }
768
769#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
770 /* The host wants to read URI data. */
771 case VBOX_CLIPBOARD_WM_URI_START_READ:
772 {
773 LogFunc(("VBOX_CLIPBOARD_WM_URI_START_READ: cTransfersRunning=%RU32\n",
774 SharedClipboardURICtxGetRunningTransfers(&pCtx->URI)));
775
776 int rc = VBoxClipboardWinOpen(hwnd);
777 if (RT_SUCCESS(rc))
778 {
779 PSHAREDCLIPBOARDURITRANSFER pTransfer;
780 rc = SharedClipboardURITransferCreate(SHAREDCLIPBOARDURITRANSFERDIR_WRITE,
781 SHAREDCLIPBOARDSOURCE_LOCAL,
782 &pTransfer);
783 if (RT_SUCCESS(rc))
784 {
785 rc = SharedClipboardURICtxTransferAdd(&pCtx->URI, pTransfer);
786 if (RT_SUCCESS(rc))
787 {
788 /* The data data in CF_HDROP format, as the files are locally present and don't need to be
789 * presented as a IDataObject or IStream. */
790 HANDLE hClip = hClip = GetClipboardData(CF_HDROP);
791 if (hClip)
792 {
793 HDROP hDrop = (HDROP)GlobalLock(hClip);
794 if (hDrop)
795 {
796 char *papszList;
797 uint32_t cbList;
798 rc = VBoxClipboardWinDropFilesToStringList((DROPFILES *)hDrop, &papszList, &cbList);
799
800 GlobalUnlock(hClip);
801
802 if (RT_SUCCESS(rc))
803 {
804 rc = SharedClipboardURILTransferSetRoots(pTransfer,
805 papszList, cbList + 1 /* Include termination */);
806 if (RT_SUCCESS(rc))
807 {
808 PVBOXCLIPBOARDURIWRITETHREADCTX pThreadCtx
809 = (PVBOXCLIPBOARDURIWRITETHREADCTX)RTMemAllocZ(sizeof(VBOXCLIPBOARDURIWRITETHREADCTX));
810 if (pThreadCtx)
811 {
812 pThreadCtx->pClipboardCtx = pCtx;
813 pThreadCtx->pTransfer = pTransfer;
814
815 rc = SharedClipboardURITransferPrepare(pTransfer);
816 if (RT_SUCCESS(rc))
817 {
818 rc = SharedClipboardURITransferRun(pTransfer, vboxClipboardURIWriteThread,
819 pThreadCtx /* pvUser */);
820 /* pThreadCtx now is owned by vboxClipboardURIWriteThread(). */
821 }
822 }
823 else
824 rc = VERR_NO_MEMORY;
825 }
826
827 if (papszList)
828 RTStrFree(papszList);
829 }
830 }
831 else
832 {
833 hClip = NULL;
834 }
835 }
836 }
837 }
838
839 VBoxClipboardWinClose();
840 }
841
842 if (RT_FAILURE(rc))
843 LogFunc(("VBOX_CLIPBOARD_WM_URI_START_READ: Failed with rc=%Rrc\n", rc));
844 break;
845 }
846
847 /* The host wants to write URI data. */
848 case VBOX_CLIPBOARD_WM_URI_START_WRITE:
849 {
850 LogFunc(("VBOX_CLIPBOARD_WM_URI_START_WRITE: cTransfersRunning=%RU32\n",
851 SharedClipboardURICtxGetRunningTransfers(&pCtx->URI)));
852
853 PSHAREDCLIPBOARDURITRANSFER pTransfer;
854 int rc = SharedClipboardURITransferCreate(SHAREDCLIPBOARDURITRANSFERDIR_READ,
855 SHAREDCLIPBOARDSOURCE_LOCAL,
856 &pTransfer);
857 if (RT_SUCCESS(rc))
858 {
859 SHAREDCLIPBOARDURITRANSFERCALLBACKS TransferCallbacks;
860 RT_ZERO(TransferCallbacks);
861
862 TransferCallbacks.pvUser = &pCtx->URI;
863 TransferCallbacks.pfnTransferComplete = vboxClipboardURITransferCompleteCallback;
864 TransferCallbacks.pfnTransferError = vboxClipboardURITransferErrorCallback;
865
866 SharedClipboardURITransferSetCallbacks(pTransfer, &TransferCallbacks);
867
868 SHAREDCLIPBOARDPROVIDERCREATIONCTX creationCtx;
869 RT_ZERO(creationCtx);
870 creationCtx.enmSource = SHAREDCLIPBOARDSOURCE_REMOTE;
871
872 RT_ZERO(creationCtx.Interface);
873 creationCtx.Interface.pfnListHdrRead = vboxClipboardURIListHdrRead;
874 creationCtx.Interface.pfnListEntryRead = vboxClipboardURIListEntryRead;
875 creationCtx.Interface.pfnObjOpen = vboxClipboardURIObjOpen;
876 creationCtx.Interface.pfnObjClose = vboxClipboardURIObjClose;
877 creationCtx.Interface.pfnObjRead = vboxClipboardURIObjRead;
878
879 creationCtx.pvUser = pCtx;
880
881 rc = SharedClipboardURITransferSetInterface(pTransfer, &creationCtx);
882 if (RT_SUCCESS(rc))
883 {
884 rc = SharedClipboardURICtxTransferAdd(&pCtx->URI, pTransfer);
885 if (RT_SUCCESS(rc))
886 {
887 if (RT_SUCCESS(rc))
888 {
889 rc = SharedClipboardURITransferPrepare(pTransfer);
890 if (RT_SUCCESS(rc))
891 {
892 rc = VBoxClipboardWinURITransferCreate(pWinCtx, pTransfer);
893 }
894 }
895 }
896 }
897 }
898
899 break;
900 }
901#endif /* VBOX_WITH_SHARED_CLIPBOARD_URI_LIST */
902
903 case WM_DESTROY:
904 {
905 LogFunc(("WM_DESTROY\n"));
906
907 int rc = VBoxClipboardWinHandleWMDestroy(pWinCtx);
908 AssertRC(rc);
909
910 /*
911 * Don't need to call PostQuitMessage cause
912 * the VBoxTray already finished a message loop.
913 */
914 }
915 break;
916
917 default:
918 {
919 LogFunc(("WM_ %p\n", msg));
920 lresultRc = DefWindowProc(hwnd, msg, wParam, lParam);
921 }
922 break;
923 }
924
925 LogFunc(("WM_ rc %d\n", lresultRc));
926 return lresultRc;
927}
928
929static LRESULT CALLBACK vboxClipboardWinWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
930
931static int vboxClipboardCreateWindow(PVBOXCLIPBOARDCONTEXT pCtx)
932{
933 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
934
935 int rc = VINF_SUCCESS;
936
937 AssertPtr(pCtx->pEnv);
938 HINSTANCE hInstance = pCtx->pEnv->hInstance;
939 Assert(hInstance != 0);
940
941 /* Register the Window Class. */
942 WNDCLASSEX wc = { 0 };
943 wc.cbSize = sizeof(WNDCLASSEX);
944
945 if (!GetClassInfoEx(hInstance, s_szClipWndClassName, &wc))
946 {
947 wc.style = CS_NOCLOSE;
948 wc.lpfnWndProc = vboxClipboardWinWndProc;
949 wc.hInstance = pCtx->pEnv->hInstance;
950 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
951 wc.lpszClassName = s_szClipWndClassName;
952
953 ATOM wndClass = RegisterClassEx(&wc);
954 if (wndClass == 0)
955 rc = RTErrConvertFromWin32(GetLastError());
956 }
957
958 if (RT_SUCCESS(rc))
959 {
960 const PVBOXCLIPBOARDWINCTX pWinCtx = &pCtx->Win;
961
962 /* Create the window. */
963 pWinCtx->hWnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
964 s_szClipWndClassName, s_szClipWndClassName,
965 WS_POPUPWINDOW,
966 -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
967 if (pWinCtx->hWnd == NULL)
968 {
969 rc = VERR_NOT_SUPPORTED;
970 }
971 else
972 {
973 SetWindowPos(pWinCtx->hWnd, HWND_TOPMOST, -200, -200, 0, 0,
974 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
975
976 VBoxClipboardWinChainAdd(pWinCtx);
977 if (!VBoxClipboardWinIsNewAPI(&pWinCtx->newAPI))
978 pWinCtx->oldAPI.timerRefresh = SetTimer(pWinCtx->hWnd, 0, 10 * 1000 /* 10s */, NULL);
979 }
980 }
981
982 LogFlowFuncLeaveRC(rc);
983 return rc;
984}
985
986static void vboxClipboardDestroy(PVBOXCLIPBOARDCONTEXT pCtx)
987{
988 AssertPtrReturnVoid(pCtx);
989
990 const PVBOXCLIPBOARDWINCTX pWinCtx = &pCtx->Win;
991
992 if (pWinCtx->hWnd)
993 {
994 DestroyWindow(pWinCtx->hWnd);
995 pWinCtx->hWnd = NULL;
996 }
997
998 UnregisterClass(s_szClipWndClassName, pCtx->pEnv->hInstance);
999}
1000
1001static LRESULT CALLBACK vboxClipboardWinWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1002{
1003 PVBOXCLIPBOARDCONTEXT pCtx = &g_Ctx; /** @todo r=andy Make pCtx available through SetWindowLongPtr() / GWL_USERDATA. */
1004 AssertPtr(pCtx);
1005
1006 /* Forward with proper context. */
1007 return vboxClipboardWinProcessMsg(pCtx, hWnd, uMsg, wParam, lParam);
1008}
1009
1010DECLCALLBACK(int) VBoxClipboardInit(const PVBOXSERVICEENV pEnv, void **ppInstance)
1011{
1012 LogFlowFuncEnter();
1013
1014 PVBOXCLIPBOARDCONTEXT pCtx = &g_Ctx; /* Only one instance for now. */
1015 AssertPtr(pCtx);
1016
1017 if (pCtx->pEnv)
1018 {
1019 /* Clipboard was already initialized. 2 or more instances are not supported. */
1020 return VERR_NOT_SUPPORTED;
1021 }
1022
1023 if (VbglR3AutoLogonIsRemoteSession())
1024 {
1025 /* Do not use clipboard for remote sessions. */
1026 LogRel(("Clipboard: Clipboard has been disabled for a remote session\n"));
1027 return VERR_NOT_SUPPORTED;
1028 }
1029
1030 pCtx->pEnv = pEnv;
1031
1032 int rc = VINF_SUCCESS;
1033
1034#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
1035 HRESULT hr = OleInitialize(NULL);
1036 if (FAILED(hr))
1037 {
1038 LogRel(("Clipboard: Initializing OLE failed (%Rhrc) -- file transfers unavailable\n"));
1039 /* Not critical, the rest of the clipboard might work. */
1040 }
1041 else
1042 LogRel(("Clipboard: Initialized OLE\n"));
1043#endif
1044
1045 if (RT_SUCCESS(rc))
1046 {
1047 /* Check if new Clipboard API is available. */
1048 /* ignore rc */ VBoxClipboardWinCheckAndInitNewAPI(&pCtx->Win.newAPI);
1049
1050 rc = VbglR3ClipboardConnect(&pCtx->CmdCtx.uClientID);
1051 if (RT_SUCCESS(rc))
1052 {
1053 pCtx->CmdCtx.uProtocol = 0;
1054
1055 rc = vboxClipboardCreateWindow(pCtx);
1056 if (RT_SUCCESS(rc))
1057 {
1058#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
1059 rc = SharedClipboardURICtxInit(&pCtx->URI);
1060 if (RT_SUCCESS(rc))
1061#endif
1062 *ppInstance = pCtx;
1063 }
1064 else
1065 {
1066 VbglR3ClipboardDisconnect(pCtx->CmdCtx.uClientID);
1067 }
1068 }
1069 }
1070
1071 LogFlowFuncLeaveRC(rc);
1072 return rc;
1073}
1074
1075DECLCALLBACK(int) VBoxClipboardWorker(void *pInstance, bool volatile *pfShutdown)
1076{
1077 AssertPtr(pInstance);
1078 LogFlowFunc(("pInstance=%p\n", pInstance));
1079
1080 /*
1081 * Tell the control thread that it can continue
1082 * spawning services.
1083 */
1084 RTThreadUserSignal(RTThreadSelf());
1085
1086 const PVBOXCLIPBOARDCONTEXT pCtx = (PVBOXCLIPBOARDCONTEXT)pInstance;
1087 AssertPtr(pCtx);
1088
1089 const PVBOXCLIPBOARDWINCTX pWinCtx = &pCtx->Win;
1090
1091 int rc;
1092
1093 /* The thread waits for incoming messages from the host. */
1094 for (;;)
1095 {
1096 LogFlowFunc(("Waiting for host message ...\n"));
1097
1098 uint32_t u32Msg;
1099 uint32_t u32Formats;
1100 rc = VbglR3ClipboardGetHostMsgOld(pCtx->CmdCtx.uClientID, &u32Msg, &u32Formats);
1101 if (RT_FAILURE(rc))
1102 {
1103 if (rc == VERR_INTERRUPTED)
1104 break;
1105
1106 LogFunc(("Error getting host message, rc=%Rrc\n", rc));
1107
1108 if (*pfShutdown)
1109 break;
1110
1111 /* Wait a bit before retrying. */
1112 RTThreadSleep(1000);
1113 continue;
1114 }
1115 else
1116 {
1117 LogFlowFunc(("u32Msg=%RU32, u32Formats=0x%x\n", u32Msg, u32Formats));
1118 switch (u32Msg)
1119 {
1120 case VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS:
1121 {
1122 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS\n"));
1123
1124 /* The host has announced available clipboard formats.
1125 * Forward the information to the window, so it can later
1126 * respond to WM_RENDERFORMAT message. */
1127 ::PostMessage(pWinCtx->hWnd, VBOX_CLIPBOARD_WM_SET_FORMATS, 0, u32Formats);
1128 break;
1129 }
1130
1131 case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
1132 {
1133 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA\n"));
1134
1135 /* The host needs data in the specified format. */
1136 ::PostMessage(pWinCtx->hWnd, VBOX_CLIPBOARD_WM_READ_DATA, 0, u32Formats);
1137 break;
1138 }
1139
1140#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
1141 case VBOX_SHARED_CLIPBOARD_HOST_MSG_URI_TRANSFER_START:
1142 {
1143 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_URI_TRANSFER_START\n"));
1144
1145 const UINT uMsg = u32Formats == 0 ?
1146 VBOX_CLIPBOARD_WM_URI_START_READ : VBOX_CLIPBOARD_WM_URI_START_WRITE;
1147
1148 ::PostMessage(pWinCtx->hWnd, uMsg, 0 /* wParm */, 0 /* lParm */);
1149 break;
1150 }
1151#endif
1152 case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
1153 {
1154 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT\n"));
1155
1156 /* The host is terminating. */
1157 LogRel(("Clipboard: Terminating ...\n"));
1158 ASMAtomicXchgBool(pfShutdown, true);
1159 break;
1160 }
1161
1162 default:
1163 {
1164 LogFlowFunc(("Unsupported message from host, message=%RU32\n", u32Msg));
1165
1166 /* Wait a bit before retrying. */
1167 RTThreadSleep(1000);
1168 break;
1169 }
1170 }
1171 }
1172
1173 if (*pfShutdown)
1174 break;
1175 }
1176
1177 LogFlowFuncLeaveRC(rc);
1178 return rc;
1179}
1180
1181DECLCALLBACK(int) VBoxClipboardStop(void *pInstance)
1182{
1183 AssertPtrReturn(pInstance, VERR_INVALID_POINTER);
1184
1185 LogFunc(("Stopping pInstance=%p\n", pInstance));
1186
1187 PVBOXCLIPBOARDCONTEXT pCtx = (PVBOXCLIPBOARDCONTEXT)pInstance;
1188 AssertPtr(pCtx);
1189
1190 VbglR3ClipboardDisconnect(pCtx->CmdCtx.uClientID);
1191 pCtx->CmdCtx.uClientID = 0;
1192
1193 LogFlowFuncLeaveRC(VINF_SUCCESS);
1194 return VINF_SUCCESS;
1195}
1196
1197DECLCALLBACK(void) VBoxClipboardDestroy(void *pInstance)
1198{
1199 AssertPtrReturnVoid(pInstance);
1200
1201 PVBOXCLIPBOARDCONTEXT pCtx = (PVBOXCLIPBOARDCONTEXT)pInstance;
1202 AssertPtr(pCtx);
1203
1204 /* Make sure that we are disconnected. */
1205 Assert(pCtx->CmdCtx.uClientID == 0);
1206
1207 vboxClipboardDestroy(pCtx);
1208
1209#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
1210 OleSetClipboard(NULL); /* Make sure to flush the clipboard on destruction. */
1211 OleUninitialize();
1212
1213 SharedClipboardURICtxDestroy(&pCtx->URI);
1214#endif
1215
1216 return;
1217}
1218
1219/**
1220 * The service description.
1221 */
1222VBOXSERVICEDESC g_SvcDescClipboard =
1223{
1224 /* pszName. */
1225 "clipboard",
1226 /* pszDescription. */
1227 "Shared Clipboard",
1228 /* methods */
1229 VBoxClipboardInit,
1230 VBoxClipboardWorker,
1231 VBoxClipboardStop,
1232 VBoxClipboardDestroy
1233};
1234
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