VirtualBox

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

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