VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-win.cpp@ 79033

Last change on this file since 79033 was 78974, checked in by vboxsync, 6 years ago

Shared Clipboard/URI: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.7 KB
Line 
1/* $Id: VBoxSharedClipboardSvc-win.cpp 78974 2019-06-04 16:51:48Z vboxsync $ */
2/** @file
3 * Shared Clipboard Service - Win32 host.
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 <iprt/win/windows.h>
24
25#include <VBox/HostServices/VBoxClipboardSvc.h>
26#include <VBox/GuestHost/SharedClipboard-win.h>
27#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
28# include <VBox/GuestHost/SharedClipboard-uri.h>
29#endif
30
31#include <iprt/alloc.h>
32#include <iprt/string.h>
33#include <iprt/asm.h>
34#include <iprt/assert.h>
35#include <iprt/ldr.h>
36#include <iprt/semaphore.h>
37#include <iprt/thread.h>
38#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
39# include <iprt/utf16.h>
40#endif
41
42#include <process.h>
43#include <shlobj.h> /* Needed for shell objects. */
44
45#include "VBoxSharedClipboardSvc-internal.h"
46
47/** Static window class name. */
48static char s_szClipWndClassName[] = VBOX_CLIPBOARD_WNDCLASS_NAME;
49
50
51/*********************************************************************************************************************************
52* Internal Functions *
53*********************************************************************************************************************************/
54static int vboxClipboardWinSyncInternal(PVBOXCLIPBOARDCONTEXT pCtx);
55
56struct _VBOXCLIPBOARDCONTEXT
57{
58 /** Handle for window message handling thread. */
59 RTTHREAD hThread;
60 /** Event which gets triggered if the host clipboard needs to render its data. */
61 RTSEMEVENT hRenderEvent;
62 /** Structure for keeping and communicating with client data (from the guest). */
63 PVBOXCLIPBOARDCLIENTDATA pClientData;
64 /** Windows-specific context data. */
65 VBOXCLIPBOARDWINCTX Win;
66#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
67 /** Structure for keeping URI clipboard information around. */
68 SHAREDCLIPBOARDURICTX URI;
69#endif
70};
71
72/* Only one client is supported. There seems to be no need for more clients. */
73static VBOXCLIPBOARDCONTEXT g_ctx;
74
75
76#ifdef LOG_ENABLED
77static void vboxClipboardDump(const void *pv, size_t cb, uint32_t u32Format)
78{
79 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
80 {
81 LogFunc(("VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT:\n"));
82 if (pv && cb)
83 LogFunc(("%ls\n", pv));
84 else
85 LogFunc(("%p %zu\n", pv, cb));
86 }
87 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
88 LogFunc(("VBOX_SHARED_CLIPBOARD_FMT_BITMAP\n"));
89 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML)
90 {
91 LogFunc(("VBOX_SHARED_CLIPBOARD_FMT_HTML:\n"));
92 if (pv && cb)
93 {
94 LogFunc(("%s\n", pv));
95
96 //size_t cb = RTStrNLen(pv, );
97 char *pszBuf = (char *)RTMemAllocZ(cb + 1);
98 RTStrCopy(pszBuf, cb + 1, (const char *)pv);
99 for (size_t off = 0; off < cb; ++off)
100 {
101 if (pszBuf[off] == '\n' || pszBuf[off] == '\r')
102 pszBuf[off] = ' ';
103 }
104
105 LogFunc(("%s\n", pszBuf));
106 RTMemFree(pszBuf);
107 }
108 else
109 LogFunc(("%p %zu\n", pv, cb));
110 }
111 else
112 LogFunc(("Invalid format %02X\n", u32Format));
113}
114#else /* !LOG_ENABLED */
115# define vboxClipboardDump(__pv, __cb, __format) do { NOREF(__pv); NOREF(__cb); NOREF(__format); } while (0)
116#endif /* !LOG_ENABLED */
117
118/** @todo Someone please explain the protocol wrt overflows... */
119static void vboxClipboardGetData(uint32_t u32Format, const void *pvSrc, uint32_t cbSrc,
120 void *pvDst, uint32_t cbDst, uint32_t *pcbActualDst)
121{
122 LogFlowFunc(("cbSrc = %d, cbDst = %d\n", cbSrc, cbDst));
123
124 if ( u32Format == VBOX_SHARED_CLIPBOARD_FMT_HTML
125 && VBoxClipboardWinIsCFHTML((const char *)pvSrc))
126 {
127 /** @todo r=bird: Why the double conversion? */
128 char *pszBuf = NULL;
129 uint32_t cbBuf = 0;
130 int rc = VBoxClipboardWinConvertCFHTMLToMIME((const char *)pvSrc, cbSrc, &pszBuf, &cbBuf);
131 if (RT_SUCCESS(rc))
132 {
133 *pcbActualDst = cbBuf;
134 if (cbBuf > cbDst)
135 {
136 /* Do not copy data. The dst buffer is not enough. */
137 RTMemFree(pszBuf);
138 return;
139 }
140 memcpy(pvDst, pszBuf, cbBuf);
141 RTMemFree(pszBuf);
142 }
143 else
144 *pcbActualDst = 0;
145 }
146 else
147 {
148 *pcbActualDst = cbSrc;
149
150 if (cbSrc > cbDst)
151 {
152 /* Do not copy data. The dst buffer is not enough. */
153 return;
154 }
155
156 memcpy(pvDst, pvSrc, cbSrc);
157 }
158
159 vboxClipboardDump(pvDst, cbSrc, u32Format);
160
161 return;
162}
163
164static int vboxClipboardReadDataFromClient(VBOXCLIPBOARDCONTEXT *pCtx, VBOXCLIPBOARDFORMAT fFormat)
165{
166 Assert(pCtx->pClientData);
167 Assert(pCtx->hRenderEvent);
168 Assert(pCtx->pClientData->State.data.pv == NULL && pCtx->pClientData->State.data.cb == 0 && pCtx->pClientData->State.data.u32Format == 0);
169
170 LogFlowFunc(("fFormat=%02X\n", fFormat));
171
172 vboxSvcClipboardReportMsg(pCtx->pClientData, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, fFormat);
173
174 return RTSemEventWait(pCtx->hRenderEvent, 30 * 1000 /* Timeout in ms */);
175}
176
177static LRESULT CALLBACK vboxClipboardWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
178{
179 LRESULT lresultRc = 0;
180
181 const PVBOXCLIPBOARDCONTEXT pCtx = &g_ctx;
182 const PVBOXCLIPBOARDWINCTX pWinCtx = &pCtx->Win;
183
184 switch (msg)
185 {
186 case WM_CLIPBOARDUPDATE:
187 {
188 LogFunc(("WM_CLIPBOARDUPDATE\n"));
189
190 if (GetClipboardOwner() != hwnd)
191 {
192 /* Clipboard was updated by another application, retrieve formats and report back. */
193 int rc = vboxClipboardWinSyncInternal(pCtx);
194 AssertRC(rc);
195 }
196 } break;
197
198 case WM_CHANGECBCHAIN:
199 {
200 LogFunc(("WM_CHANGECBCHAIN\n"));
201
202 if (VBoxClipboardWinIsNewAPI(&pWinCtx->newAPI))
203 {
204 lresultRc = DefWindowProc(hwnd, msg, wParam, lParam);
205 break;
206 }
207
208 HWND hwndRemoved = (HWND)wParam;
209 HWND hwndNext = (HWND)lParam;
210
211 if (hwndRemoved == pWinCtx->hWndNextInChain)
212 {
213 /* The window that was next to our in the chain is being removed.
214 * Relink to the new next window.
215 */
216 pWinCtx->hWndNextInChain = hwndNext;
217 }
218 else
219 {
220 if (pWinCtx->hWndNextInChain)
221 {
222 /* Pass the message further. */
223 DWORD_PTR dwResult;
224 lresultRc = SendMessageTimeout(pWinCtx->hWndNextInChain, WM_CHANGECBCHAIN, wParam, lParam, 0,
225 VBOX_CLIPBOARD_CBCHAIN_TIMEOUT_MS,
226 &dwResult);
227 if (!lresultRc)
228 lresultRc = (LRESULT)dwResult;
229 }
230 }
231 } break;
232
233 case WM_DRAWCLIPBOARD:
234 {
235 LogFunc(("WM_DRAWCLIPBOARD\n"));
236
237 if (GetClipboardOwner() != hwnd)
238 {
239 /* Clipboard was updated by another application, retrieve formats and report back. */
240 int vboxrc = vboxClipboardWinSyncInternal(pCtx);
241 AssertRC(vboxrc);
242 }
243
244 if (pWinCtx->hWndNextInChain)
245 {
246 LogFunc(("WM_DRAWCLIPBOARD next %p\n", pWinCtx->hWndNextInChain));
247
248 /* Pass the message to next windows in the clipboard chain. */
249 DWORD_PTR dwResult;
250 lresultRc = SendMessageTimeout(pWinCtx->hWndNextInChain, msg, wParam, lParam, 0, VBOX_CLIPBOARD_CBCHAIN_TIMEOUT_MS,
251 &dwResult);
252 if (!lresultRc)
253 lresultRc = dwResult;
254 }
255 } break;
256
257 case WM_TIMER:
258 {
259 if (VBoxClipboardWinIsNewAPI(&pWinCtx->newAPI))
260 break;
261
262 HWND hViewer = GetClipboardViewer();
263
264 /* Re-register ourselves in the clipboard chain if our last ping
265 * timed out or there seems to be no valid chain. */
266 if (!hViewer || pWinCtx->oldAPI.fCBChainPingInProcess)
267 {
268 VBoxClipboardWinRemoveFromCBChain(&pCtx->Win);
269 VBoxClipboardWinAddToCBChain(&pCtx->Win);
270 }
271
272 /* Start a new ping by passing a dummy WM_CHANGECBCHAIN to be
273 * processed by ourselves to the chain. */
274 pWinCtx->oldAPI.fCBChainPingInProcess = TRUE;
275
276 hViewer = GetClipboardViewer();
277 if (hViewer)
278 SendMessageCallback(hViewer, WM_CHANGECBCHAIN,
279 (WPARAM)pWinCtx->hWndNextInChain, (LPARAM)pWinCtx->hWndNextInChain,
280 VBoxClipboardWinChainPingProc, (ULONG_PTR)pWinCtx);
281 } break;
282
283 case WM_RENDERFORMAT:
284 {
285 LogFunc(("WM_RENDERFORMAT\n"));
286
287 /* Insert the requested clipboard format data into the clipboard. */
288 const UINT cfFormat = (UINT)wParam;
289
290 const VBOXCLIPBOARDFORMAT fFormat = VBoxClipboardWinClipboardFormatToVBox(cfFormat);
291
292 LogFunc(("WM_RENDERFORMAT: cfFormat=%u -> fFormat=0x%x\n", cfFormat, fFormat));
293
294 if ( fFormat == VBOX_SHARED_CLIPBOARD_FMT_NONE
295 || pCtx->pClientData == NULL)
296 {
297 /* Unsupported clipboard format is requested. */
298 LogFunc(("WM_RENDERFORMAT unsupported format requested or client is not active\n"));
299 VBoxClipboardWinClear();
300 }
301 else
302 {
303 int rc = vboxClipboardReadDataFromClient(pCtx, fFormat);
304
305 LogFunc(("vboxClipboardReadDataFromClient rc = %Rrc, pv %p, cb %d, u32Format %d\n",
306 rc, pCtx->pClientData->State.data.pv, pCtx->pClientData->State.data.cb, pCtx->pClientData->State.data.u32Format));
307
308 if ( RT_SUCCESS (rc)
309 && pCtx->pClientData->State.data.pv != NULL
310 && pCtx->pClientData->State.data.cb > 0
311 && pCtx->pClientData->State.data.u32Format == fFormat)
312 {
313 HANDLE hMem = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, pCtx->pClientData->State.data.cb);
314
315 LogFunc(("hMem %p\n", hMem));
316
317 if (hMem)
318 {
319 void *pMem = GlobalLock(hMem);
320
321 LogFunc(("pMem %p, GlobalSize %d\n", pMem, GlobalSize(hMem)));
322
323 if (pMem)
324 {
325 LogFunc(("WM_RENDERFORMAT setting data\n"));
326
327 if (pCtx->pClientData->State.data.pv)
328 {
329 memcpy(pMem, pCtx->pClientData->State.data.pv, pCtx->pClientData->State.data.cb);
330
331 RTMemFree(pCtx->pClientData->State.data.pv);
332 pCtx->pClientData->State.data.pv = NULL;
333 }
334
335 pCtx->pClientData->State.data.cb = 0;
336 pCtx->pClientData->State.data.u32Format = 0;
337
338 /* The memory must be unlocked before inserting to the Clipboard. */
339 GlobalUnlock(hMem);
340
341 /* 'hMem' contains the host clipboard data.
342 * size is 'cb' and format is 'format'.
343 */
344 HANDLE hClip = SetClipboardData(cfFormat, hMem);
345
346 LogFunc(("vboxClipboardHostEvent hClip %p\n", hClip));
347
348 if (hClip)
349 {
350 /* The hMem ownership has gone to the system. Nothing to do. */
351 break;
352 }
353 }
354
355 GlobalFree(hMem);
356 }
357 }
358
359 RTMemFree(pCtx->pClientData->State.data.pv);
360 pCtx->pClientData->State.data.pv = NULL;
361 pCtx->pClientData->State.data.cb = 0;
362 pCtx->pClientData->State.data.u32Format = 0;
363
364 /* Something went wrong. */
365 VBoxClipboardWinClear();
366 }
367 } break;
368
369 case WM_RENDERALLFORMATS:
370 {
371 LogFunc(("WM_RENDERALLFORMATS\n"));
372
373 /* Do nothing. The clipboard formats will be unavailable now, because the
374 * windows is to be destroyed and therefore the guest side becomes inactive.
375 */
376 int rc = VBoxClipboardWinOpen(hwnd);
377 if (RT_SUCCESS(rc))
378 {
379 VBoxClipboardWinClear();
380 VBoxClipboardWinClose();
381 }
382 } break;
383
384 case VBOX_CLIPBOARD_WM_SET_FORMATS:
385 {
386 if ( pCtx->pClientData == NULL
387 || pCtx->pClientData->State.fHostMsgFormats)
388 {
389 /* Host has pending formats message. Ignore the guest announcement,
390 * because host clipboard has more priority.
391 */
392 LogFunc(("VBOX_CLIPBOARD_WM_SET_FORMATS ignored\n"));
393 break;
394 }
395
396 /* Announce available formats. Do not insert data, they will be inserted in WM_RENDER*. */
397 VBOXCLIPBOARDFORMATS fFormats = (uint32_t)lParam;
398
399 LogFunc(("VBOX_CLIPBOARD_WM_SET_FORMATS: fFormats=%02X\n", fFormats));
400
401 int rc = VBoxClipboardWinOpen(hwnd);
402 if (RT_SUCCESS(rc))
403 {
404 VBoxClipboardWinClear();
405
406 HANDLE hClip = NULL;
407 UINT cfFormat = 0;
408
409 /** @todo r=andy Only one clipboard format can be set at once, at least on Windows. */
410
411 if (fFormats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
412 {
413 hClip = SetClipboardData(CF_UNICODETEXT, NULL);
414 }
415 else if (fFormats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
416 {
417 hClip = SetClipboardData(CF_DIB, NULL);
418 }
419 else if (fFormats & VBOX_SHARED_CLIPBOARD_FMT_HTML)
420 {
421 cfFormat = RegisterClipboardFormat(VBOX_CLIPBOARD_WIN_REGFMT_HTML);
422 if (cfFormat)
423 hClip = SetClipboardData(cfFormat, NULL);
424 }
425
426#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
427 /** @todo */
428#endif
429 VBoxClipboardWinClose();
430
431 LogFunc(("VBOX_CLIPBOARD_WM_SET_FORMATS: hClip=%p, lastErr=%ld\n", hClip, GetLastError ()));
432 }
433 } break;
434
435 case WM_DESTROY:
436 {
437 /* MS recommends to remove from Clipboard chain in this callback. */
438 VBoxClipboardWinRemoveFromCBChain(&pCtx->Win);
439 if (pWinCtx->oldAPI.timerRefresh)
440 {
441 Assert(pWinCtx->hWnd);
442 KillTimer(pWinCtx->hWnd, 0);
443 }
444 PostQuitMessage(0);
445 } break;
446
447 default:
448 {
449 LogFunc(("WM_ %p\n", msg));
450 lresultRc = DefWindowProc(hwnd, msg, wParam, lParam);
451 }
452 }
453
454 LogFunc(("WM_ rc %d\n", lresultRc));
455 return lresultRc;
456}
457
458DECLCALLBACK(int) VBoxClipboardThread(RTTHREAD hThreadSelf, void *pvUser)
459{
460 RT_NOREF(hThreadSelf, pvUser);
461
462 /* Create a window and make it a clipboard viewer. */
463 int rc = VINF_SUCCESS;
464
465 LogFlowFuncEnter();
466
467 const PVBOXCLIPBOARDCONTEXT pCtx = &g_ctx;
468 const PVBOXCLIPBOARDWINCTX pWinCtx = &pCtx->Win;
469
470 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
471
472 /* Register the Window Class. */
473 WNDCLASS wc;
474 RT_ZERO(wc);
475
476 wc.style = CS_NOCLOSE;
477 wc.lpfnWndProc = vboxClipboardWndProc;
478 wc.hInstance = hInstance;
479 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
480 wc.lpszClassName = s_szClipWndClassName;
481
482 ATOM atomWindowClass = RegisterClass(&wc);
483
484 if (atomWindowClass == 0)
485 {
486 LogFunc(("Failed to register window class\n"));
487 rc = VERR_NOT_SUPPORTED;
488 }
489 else
490 {
491 /* Create the window. */
492 pWinCtx->hWnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
493 s_szClipWndClassName, s_szClipWndClassName,
494 WS_POPUPWINDOW,
495 -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
496 if (pWinCtx->hWnd == NULL)
497 {
498 LogFunc(("Failed to create window\n"));
499 rc = VERR_NOT_SUPPORTED;
500 }
501 else
502 {
503 SetWindowPos(pWinCtx->hWnd, HWND_TOPMOST, -200, -200, 0, 0,
504 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
505
506 VBoxClipboardWinAddToCBChain(&pCtx->Win);
507 if (!VBoxClipboardWinIsNewAPI(&pWinCtx->newAPI))
508 pWinCtx->oldAPI.timerRefresh = SetTimer(pWinCtx->hWnd, 0, 10 * 1000, NULL);
509
510 MSG msg;
511 BOOL msgret = 0;
512 while ((msgret = GetMessage(&msg, NULL, 0, 0)) > 0)
513 {
514 TranslateMessage(&msg);
515 DispatchMessage(&msg);
516 }
517 /*
518 * Window procedure can return error,
519 * but this is exceptional situation
520 * that should be identified in testing
521 */
522 Assert(msgret >= 0);
523 LogFunc(("Message loop finished. GetMessage returned %d, message id: %d \n", msgret, msg.message));
524 }
525 }
526
527 pWinCtx->hWnd = NULL;
528
529 if (atomWindowClass != 0)
530 {
531 UnregisterClass(s_szClipWndClassName, hInstance);
532 atomWindowClass = 0;
533 }
534
535 return 0;
536}
537
538/**
539 * Synchronizes the host and the guest clipboard formats by sending all supported host clipboard
540 * formats to the guest.
541 *
542 * @returns VBox status code, VINF_NO_CHANGE if no synchronization was required.
543 * @param pCtx Clipboard context to synchronize.
544 */
545static int vboxClipboardWinSyncInternal(PVBOXCLIPBOARDCONTEXT pCtx)
546{
547 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
548
549 int rc;
550
551 if (pCtx->pClientData)
552 {
553 uint32_t uFormats;
554 rc = VBoxClipboardWinGetFormats(&pCtx->Win, &uFormats);
555 if (RT_SUCCESS(rc))
556 vboxSvcClipboardReportMsg(pCtx->pClientData, VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS, uFormats);
557 }
558 else /* If we don't have any client data (yet), bail out. */
559 rc = VINF_NO_CHANGE;
560
561 LogFlowFuncLeaveRC(rc);
562 return rc;
563}
564
565/*
566 * Public platform dependent functions.
567 */
568int VBoxClipboardSvcImplInit(void)
569{
570 RT_ZERO(g_ctx); /* Be careful not messing up non-POD types! */
571
572 /* Check that new Clipboard API is available. */
573 VBoxClipboardWinCheckAndInitNewAPI(&g_ctx.Win.newAPI);
574
575 int rc = RTSemEventCreate(&g_ctx.hRenderEvent);
576 if (RT_SUCCESS(rc))
577 {
578 rc = RTThreadCreate(&g_ctx.hThread, VBoxClipboardThread, NULL, _64K /* Stack size */,
579 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
580 }
581
582 if (RT_FAILURE(rc))
583 RTSemEventDestroy(g_ctx.hRenderEvent);
584
585 return rc;
586}
587
588void VBoxClipboardSvcImplDestroy(void)
589{
590 LogFlowFuncEnter();
591
592 if (g_ctx.Win.hWnd)
593 {
594 PostMessage(g_ctx.Win.hWnd, WM_CLOSE, 0, 0);
595 }
596
597 int rc = RTSemEventDestroy(g_ctx.hRenderEvent);
598 AssertRC(rc);
599
600 /* Wait for the window thread to terminate. */
601 rc = RTThreadWait(g_ctx.hThread, 30 * 1000 /* Timeout in ms */, NULL);
602 if (RT_FAILURE(rc))
603 LogRel(("Shared Clipboard: Waiting for window thread termination failed with rc=%Rrc\n", rc));
604
605 g_ctx.hThread = NIL_RTTHREAD;
606}
607
608int VBoxClipboardSvcImplConnect(PVBOXCLIPBOARDCLIENTDATA pClientData, bool fHeadless)
609{
610 RT_NOREF(fHeadless);
611
612 LogFlowFuncEnter();
613
614 if (g_ctx.pClientData != NULL)
615 {
616 /* One client only. */
617 return VERR_NOT_SUPPORTED;
618 }
619
620 pClientData->State.pCtx = &g_ctx;
621
622 pClientData->State.pCtx->pClientData = pClientData;
623
624 /* Sync the host clipboard content with the client. */
625 VBoxClipboardSvcImplSync(pClientData);
626
627 return VINF_SUCCESS;
628}
629
630int VBoxClipboardSvcImplSync(PVBOXCLIPBOARDCLIENTDATA pClientData)
631{
632 /* Sync the host clipboard content with the client. */
633 return vboxClipboardWinSyncInternal(pClientData->State.pCtx);
634}
635
636void VBoxClipboardSvcImplDisconnect(PVBOXCLIPBOARDCLIENTDATA pClientData)
637{
638 RT_NOREF(pClientData);
639
640 LogFlowFuncEnter();
641
642 g_ctx.pClientData = NULL;
643}
644
645void VBoxClipboardSvcImplFormatAnnounce(PVBOXCLIPBOARDCLIENTDATA pClientData, uint32_t u32Formats)
646{
647 AssertPtrReturnVoid(pClientData);
648 AssertPtrReturnVoid(pClientData->State.pCtx);
649
650 /*
651 * The guest announces formats. Forward to the window thread.
652 */
653 PostMessage(pClientData->State.pCtx->Win.hWnd, WM_USER, 0, u32Formats);
654}
655
656#ifdef VBOX_STRICT
657static int vboxClipboardDbgDumpHtml(const char *pszSrc, size_t cb)
658{
659 size_t cchIgnored = 0;
660 int rc = RTStrNLenEx(pszSrc, cb, &cchIgnored);
661 if (RT_SUCCESS(rc))
662 {
663 char *pszBuf = (char *)RTMemAllocZ(cb + 1);
664 if (pszBuf != NULL)
665 {
666 rc = RTStrCopy(pszBuf, cb + 1, (const char *)pszSrc);
667 if (RT_SUCCESS(rc))
668 {
669 for (size_t i = 0; i < cb; ++i)
670 if (pszBuf[i] == '\n' || pszBuf[i] == '\r')
671 pszBuf[i] = ' ';
672 }
673 else
674 LogFunc(("Error in copying string\n"));
675 LogFunc(("Removed \\r\\n: %s\n", pszBuf));
676 RTMemFree(pszBuf);
677 }
678 else
679 {
680 rc = VERR_NO_MEMORY;
681 LogFunc(("Not enough memory to allocate buffer\n"));
682 }
683 }
684 return rc;
685}
686#endif
687
688int VBoxClipboardSvcImplReadData(PVBOXCLIPBOARDCLIENTDATA pClientData, uint32_t u32Format, void *pv, uint32_t cb, uint32_t *pcbActual)
689{
690 AssertPtrReturn(pClientData, VERR_INVALID_POINTER);
691 AssertPtrReturn(pClientData->State.pCtx, VERR_INVALID_POINTER);
692
693 LogFlowFunc(("u32Format=%02X\n", u32Format));
694
695 HANDLE hClip = NULL;
696
697 const PVBOXCLIPBOARDWINCTX pWinCtx = &pClientData->State.pCtx->Win;
698
699 /*
700 * The guest wants to read data in the given format.
701 */
702 int rc = VBoxClipboardWinOpen(pWinCtx->hWnd);
703 if (RT_SUCCESS(rc))
704 {
705 LogFunc(("Clipboard opened\n"));
706
707 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
708 {
709 hClip = GetClipboardData(CF_DIB);
710 if (hClip != NULL)
711 {
712 LPVOID lp = GlobalLock(hClip);
713
714 if (lp != NULL)
715 {
716 LogFunc(("CF_DIB\n"));
717
718 vboxClipboardGetData(VBOX_SHARED_CLIPBOARD_FMT_BITMAP, lp, GlobalSize(hClip),
719 pv, cb, pcbActual);
720
721 GlobalUnlock(hClip);
722 }
723 else
724 {
725 hClip = NULL;
726 }
727 }
728 }
729 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
730 {
731 hClip = GetClipboardData(CF_UNICODETEXT);
732 if (hClip != NULL)
733 {
734 LPWSTR uniString = (LPWSTR)GlobalLock(hClip);
735
736 if (uniString != NULL)
737 {
738 LogFunc(("CF_UNICODETEXT\n"));
739
740 vboxClipboardGetData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, uniString, (lstrlenW(uniString) + 1) * 2,
741 pv, cb, pcbActual);
742
743 GlobalUnlock(hClip);
744 }
745 else
746 {
747 hClip = NULL;
748 }
749 }
750 }
751 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML)
752 {
753 UINT format = RegisterClipboardFormat(VBOX_CLIPBOARD_WIN_REGFMT_HTML);
754 if (format != 0)
755 {
756 hClip = GetClipboardData(format);
757 if (hClip != NULL)
758 {
759 LPVOID lp = GlobalLock(hClip);
760 if (lp != NULL)
761 {
762 /** @todo r=andy Add data overflow handling. */
763 vboxClipboardGetData(VBOX_SHARED_CLIPBOARD_FMT_HTML, lp, GlobalSize(hClip),
764 pv, cb, pcbActual);
765#ifdef VBOX_STRICT
766 LogFlowFunc(("Raw HTML clipboard data from host:"));
767 vboxClipboardDbgDumpHtml((char *)pv, cb);
768#endif
769 GlobalUnlock(hClip);
770 }
771 else
772 {
773 hClip = NULL;
774 }
775 }
776 }
777 }
778#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
779 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_URI_LIST)
780 {
781 hClip = GetClipboardData(CF_HDROP);
782 if (hClip != NULL) /* Do we have data in CF_HDROP format? */
783 {
784 LPVOID lp = GlobalLock(hClip);
785 if (lp)
786 {
787 void *pvTemp;
788 size_t cbTemp;
789 rc = VBoxClipboardWinDropFilesToStringList((DROPFILES *)lp, (char **)&pvTemp, &cbTemp);
790 if (RT_SUCCESS(rc))
791 {
792 if (cbTemp > cb) /** @todo Add overflow handling! */
793 {
794 AssertMsgFailed(("More data buffer needed -- fix this\n"));
795 cbTemp = cb; /* Never copy more than the available buffer supplies. */
796 }
797
798 memcpy(pv, pvTemp, cbTemp);
799
800 RTMemFree(pvTemp);
801
802 *pcbActual = (uint32_t)cbTemp;
803 }
804
805 GlobalUnlock(hClip);
806 }
807 }
808 }
809#endif /* VBOX_WITH_SHARED_CLIPBOARD_URI_LIST */
810 VBoxClipboardWinClose();
811 }
812
813 if (hClip == NULL)
814 {
815 /* Reply with empty data. */
816 vboxClipboardGetData(0, NULL, 0, pv, cb, pcbActual);
817 }
818
819 return VINF_SUCCESS; /** @todo r=andy Return rc here? */
820}
821
822void VBoxClipboardSvcImplWriteData(PVBOXCLIPBOARDCLIENTDATA pClientData, void *pv, uint32_t cb, uint32_t u32Format)
823{
824 LogFlowFuncEnter();
825
826 /*
827 * The guest returns data that was requested in the WM_RENDERFORMAT handler.
828 */
829 Assert(pClientData->State.data.pv == NULL && pClientData->State.data.cb == 0 && pClientData->State.data.u32Format == 0);
830
831 vboxClipboardDump(pv, cb, u32Format);
832
833 if (cb > 0)
834 {
835 char *pszResult = NULL;
836
837 if ( u32Format == VBOX_SHARED_CLIPBOARD_FMT_HTML
838 && !VBoxClipboardWinIsCFHTML((const char*)pv))
839 {
840 /* check that this is not already CF_HTML */
841 uint32_t cbResult;
842 int rc = VBoxClipboardWinConvertMIMEToCFHTML((const char *)pv, cb, &pszResult, &cbResult);
843 if (RT_SUCCESS(rc))
844 {
845 if (pszResult != NULL && cbResult != 0)
846 {
847 pClientData->State.data.pv = pszResult;
848 pClientData->State.data.cb = cbResult;
849 pClientData->State.data.u32Format = u32Format;
850 }
851 }
852 }
853 else
854 {
855 pClientData->State.data.pv = RTMemDup(pv, cb);
856 if (pClientData->State.data.pv)
857 {
858 pClientData->State.data.cb = cb;
859 pClientData->State.data.u32Format = u32Format;
860 }
861 }
862 }
863
864 AssertPtr(pClientData->State.pCtx);
865 int rc = RTSemEventSignal(pClientData->State.pCtx->hRenderEvent);
866 AssertRC(rc);
867
868 /** @todo Return rc. */
869}
870
871#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
872int VBoxClipboardSvcImplURIReadDir(PVBOXCLIPBOARDCLIENTDATA pClientData, PVBOXCLIPBOARDDIRDATA pDirData)
873{
874 RT_NOREF(pClientData, pDirData);
875 return VERR_NOT_IMPLEMENTED;
876}
877
878int VBoxClipboardSvcImplURIWriteDir(PVBOXCLIPBOARDCLIENTDATA pClientData, PVBOXCLIPBOARDDIRDATA pDirData)
879{
880 RT_NOREF(pClientData, pDirData);
881 return VERR_NOT_IMPLEMENTED;
882}
883
884int VBoxClipboardSvcImplURIReadFileHdr(PVBOXCLIPBOARDCLIENTDATA pClientData, PVBOXCLIPBOARDFILEHDR pFileHdr)
885{
886 RT_NOREF(pClientData, pFileHdr);
887 return VERR_NOT_IMPLEMENTED;
888}
889
890int VBoxClipboardSvcImplURIWriteFileHdr(PVBOXCLIPBOARDCLIENTDATA pClientData, PVBOXCLIPBOARDFILEHDR pFileHdr)
891{
892 RT_NOREF(pClientData, pFileHdr);
893 return VERR_NOT_IMPLEMENTED;
894}
895
896int VBoxClipboardSvcImplURIReadFileData(PVBOXCLIPBOARDCLIENTDATA pClientData, PVBOXCLIPBOARDFILEDATA pFileData)
897{
898 RT_NOREF(pClientData, pFileData);
899 return VERR_NOT_IMPLEMENTED;
900}
901
902int VBoxClipboardSvcImplURIWriteFileData(PVBOXCLIPBOARDCLIENTDATA pClientData, PVBOXCLIPBOARDFILEDATA pFileData)
903{
904 RT_NOREF(pClientData, pFileData);
905 return VERR_NOT_IMPLEMENTED;
906}
907#endif /* VBOX_WITH_SHARED_CLIPBOARD_URI_LIST */
908
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