VirtualBox

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

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

Shared Clipboard/URI: Windows build fix.

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