VirtualBox

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

Last change on this file since 78425 was 78317, checked in by vboxsync, 6 years ago

Shared Clipboard/URI: Renaming (VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS -> VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.1 KB
Line 
1/* $Id: VBoxSharedClipboardSvc-win.cpp 78317 2019-04-26 09:44: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
28#include <iprt/alloc.h>
29#include <iprt/string.h>
30#include <iprt/asm.h>
31#include <iprt/assert.h>
32#include <iprt/thread.h>
33#include <iprt/ldr.h>
34#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
35# include <iprt/utf16.h>
36#endif
37
38#include <process.h>
39#include <shlobj.h> /* Needed for shell objects. */
40
41#include "VBoxClipboard.h"
42
43/** Static window class name. */
44static char s_szClipWndClassName[] = VBOX_CLIPBOARD_WNDCLASS_NAME;
45
46
47/*********************************************************************************************************************************
48* Internal Functions *
49*********************************************************************************************************************************/
50static int ConvertCFHtmlToMime(const char *pszSource, const uint32_t cch, char **ppszOutput, uint32_t *pch);
51static int ConvertMimeToCFHTML(const char *pszSource, size_t cb, char **ppszOutput, uint32_t *pcbOutput);
52static bool IsWindowsHTML(const char *source);
53static int vboxClipboardSyncInternal(PVBOXCLIPBOARDCONTEXT pCtx);
54
55struct _VBOXCLIPBOARDCONTEXT
56{
57 /** Handle for window message handling thread. */
58 RTTHREAD hThread;
59 /** Event which gets triggered if the host clipboard needs to render its data. */
60 HANDLE hRenderEvent;
61 /** Structure for keeping and communicating with client data (from the guest). */
62 PVBOXCLIPBOARDCLIENTDATA pClient;
63 /** Windows-specific context data. */
64 VBOXCLIPBOARDWINCTX Win;
65};
66
67/* Only one client is supported. There seems to be no need for more clients. */
68static VBOXCLIPBOARDCONTEXT g_ctx;
69
70
71#ifdef LOG_ENABLED
72static void vboxClipboardDump(const void *pv, size_t cb, uint32_t u32Format)
73{
74 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
75 {
76 LogFunc(("VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT:\n"));
77 if (pv && cb)
78 LogFunc(("%ls\n", pv));
79 else
80 LogFunc(("%p %zu\n", pv, cb));
81 }
82 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
83 LogFunc(("VBOX_SHARED_CLIPBOARD_FMT_BITMAP\n"));
84 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML)
85 {
86 LogFunc(("VBOX_SHARED_CLIPBOARD_FMT_HTML:\n"));
87 if (pv && cb)
88 {
89 LogFunc(("%s\n", pv));
90
91 //size_t cb = RTStrNLen(pv, );
92 char *pszBuf = (char *)RTMemAllocZ(cb + 1);
93 RTStrCopy(pszBuf, cb + 1, (const char *)pv);
94 for (size_t off = 0; off < cb; ++off)
95 {
96 if (pszBuf[off] == '\n' || pszBuf[off] == '\r')
97 pszBuf[off] = ' ';
98 }
99
100 LogFunc(("%s\n", pszBuf));
101 RTMemFree(pszBuf);
102 }
103 else
104 LogFunc(("%p %zu\n", pv, cb));
105 }
106 else
107 LogFunc(("Invalid format %02X\n", u32Format));
108}
109#else /* !LOG_ENABLED */
110# define vboxClipboardDump(__pv, __cb, __format) do { NOREF(__pv); NOREF(__cb); NOREF(__format); } while (0)
111#endif /* !LOG_ENABLED */
112
113/** @todo Someone please explain the protocol wrt overflows... */
114static void vboxClipboardGetData(uint32_t u32Format, const void *pvSrc, uint32_t cbSrc,
115 void *pvDst, uint32_t cbDst, uint32_t *pcbActualDst)
116{
117 LogFlow(("vboxClipboardGetData cbSrc = %d, cbDst = %d\n", cbSrc, cbDst));
118
119 if ( u32Format == VBOX_SHARED_CLIPBOARD_FMT_HTML
120 && IsWindowsHTML((const char *)pvSrc))
121 {
122 /** @todo r=bird: Why the double conversion? */
123 char *pszBuf = NULL;
124 uint32_t cbBuf = 0;
125 int rc = ConvertCFHtmlToMime((const char *)pvSrc, cbSrc, &pszBuf, &cbBuf);
126 if (RT_SUCCESS(rc))
127 {
128 *pcbActualDst = cbBuf;
129 if (cbBuf > cbDst)
130 {
131 /* Do not copy data. The dst buffer is not enough. */
132 RTMemFree(pszBuf);
133 return;
134 }
135 memcpy(pvDst, pszBuf, cbBuf);
136 RTMemFree(pszBuf);
137 }
138 else
139 *pcbActualDst = 0;
140 }
141#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
142 if (u32Format == VBOX_SHARED_CLIPBOARD_FMT_URI_LIST)
143 {
144 /* Convert data to URI list. */
145 }
146#endif
147 else
148 {
149 *pcbActualDst = cbSrc;
150
151 if (cbSrc > cbDst)
152 {
153 /* Do not copy data. The dst buffer is not enough. */
154 return;
155 }
156
157 memcpy(pvDst, pvSrc, cbSrc);
158 }
159
160 vboxClipboardDump(pvDst, cbSrc, u32Format);
161
162 return;
163}
164
165static int vboxClipboardReadDataFromClient(VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format)
166{
167 Assert(pCtx->pClient);
168 Assert(pCtx->hRenderEvent);
169 Assert(pCtx->pClient->data.pv == NULL && pCtx->pClient->data.cb == 0 && pCtx->pClient->data.u32Format == 0);
170
171 LogFlow(("vboxClipboardReadDataFromClient u32Format = %02X\n", u32Format));
172
173 ResetEvent(pCtx->hRenderEvent);
174
175 vboxSvcClipboardReportMsg(pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, u32Format);
176
177 DWORD ret = WaitForSingleObject(pCtx->hRenderEvent, INFINITE);
178 LogFlow(("vboxClipboardReadDataFromClient wait completed, ret 0x%08X, err %d\n",
179 ret, GetLastError())); NOREF(ret);
180
181 return VINF_SUCCESS;
182}
183
184static LRESULT CALLBACK vboxClipboardWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
185{
186 LRESULT rc = 0;
187
188 const PVBOXCLIPBOARDCONTEXT pCtx = &g_ctx;
189 const PVBOXCLIPBOARDWINCTX pWinCtx = &pCtx->Win;
190
191 switch (msg)
192 {
193 case WM_CLIPBOARDUPDATE:
194 {
195 Log(("WM_CLIPBOARDUPDATE\n"));
196
197 if (GetClipboardOwner() != hwnd)
198 {
199 /* Clipboard was updated by another application, retrieve formats and report back. */
200 int vboxrc = vboxClipboardSyncInternal(pCtx);
201 AssertRC(vboxrc);
202 }
203 } break;
204
205 case WM_CHANGECBCHAIN:
206 {
207 Log(("WM_CHANGECBCHAIN\n"));
208
209 if (VBoxClipboardWinIsNewAPI(&pWinCtx->newAPI))
210 {
211 rc = DefWindowProc(hwnd, msg, wParam, lParam);
212 break;
213 }
214
215 HWND hwndRemoved = (HWND)wParam;
216 HWND hwndNext = (HWND)lParam;
217
218 if (hwndRemoved == pWinCtx->hWndNextInChain)
219 {
220 /* The window that was next to our in the chain is being removed.
221 * Relink to the new next window.
222 */
223 pWinCtx->hWndNextInChain = hwndNext;
224 }
225 else
226 {
227 if (pWinCtx->hWndNextInChain)
228 {
229 /* Pass the message further. */
230 DWORD_PTR dwResult;
231 rc = SendMessageTimeout(pWinCtx->hWndNextInChain, WM_CHANGECBCHAIN, wParam, lParam, 0,
232 VBOX_CLIPBOARD_CBCHAIN_TIMEOUT_MS,
233 &dwResult);
234 if (!rc)
235 rc = (LRESULT)dwResult;
236 }
237 }
238 } break;
239
240 case WM_DRAWCLIPBOARD:
241 {
242 Log(("WM_DRAWCLIPBOARD\n"));
243
244 if (GetClipboardOwner() != hwnd)
245 {
246 /* Clipboard was updated by another application, retrieve formats and report back. */
247 int vboxrc = vboxClipboardSyncInternal(pCtx);
248 AssertRC(vboxrc);
249 }
250
251 if (pWinCtx->hWndNextInChain)
252 {
253 Log(("WM_DRAWCLIPBOARD next %p\n", pWinCtx->hWndNextInChain));
254 /* Pass the message to next windows in the clipboard chain. */
255 DWORD_PTR dwResult;
256 rc = SendMessageTimeout(pWinCtx->hWndNextInChain, msg, wParam, lParam, 0, VBOX_CLIPBOARD_CBCHAIN_TIMEOUT_MS,
257 &dwResult);
258 if (!rc)
259 rc = dwResult;
260 }
261 } break;
262
263 case WM_TIMER:
264 {
265 if (VBoxClipboardWinIsNewAPI(&pWinCtx->newAPI))
266 break;
267
268 HWND hViewer = GetClipboardViewer();
269
270 /* Re-register ourselves in the clipboard chain if our last ping
271 * timed out or there seems to be no valid chain. */
272 if (!hViewer || pWinCtx->oldAPI.fCBChainPingInProcess)
273 {
274 VBoxClipboardWinRemoveFromCBChain(&pCtx->Win);
275 VBoxClipboardWinAddToCBChain(&pCtx->Win);
276 }
277
278 /* Start a new ping by passing a dummy WM_CHANGECBCHAIN to be
279 * processed by ourselves to the chain. */
280 pWinCtx->oldAPI.fCBChainPingInProcess = TRUE;
281
282 hViewer = GetClipboardViewer();
283 if (hViewer)
284 SendMessageCallback(hViewer, WM_CHANGECBCHAIN,
285 (WPARAM)pWinCtx->hWndNextInChain, (LPARAM)pWinCtx->hWndNextInChain,
286 VBoxClipboardWinChainPingProc, (ULONG_PTR)pWinCtx);
287 } break;
288
289 case WM_RENDERFORMAT:
290 {
291 /* Insert the requested clipboard format data into the clipboard. */
292 uint32_t u32Format = 0;
293
294 UINT format = (UINT)wParam;
295
296 Log(("WM_RENDERFORMAT: Format %u\n", format));
297
298 switch (format)
299 {
300 case CF_UNICODETEXT:
301 u32Format |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
302 break;
303
304 case CF_DIB:
305 u32Format |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
306 break;
307
308 default:
309 if (format >= 0xC000)
310 {
311 TCHAR szFormatName[256]; /** @todo r=andy Unicode, 256 is enough? */
312 int cActual = GetClipboardFormatName(format, szFormatName, sizeof(szFormatName) / sizeof (TCHAR));
313 if (cActual)
314 {
315 if (RTStrCmp(szFormatName, "HTML Format") == 0)
316 u32Format |= VBOX_SHARED_CLIPBOARD_FMT_HTML;
317#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
318 if ( RTStrCmp(szFormatName, CFSTR_FILEDESCRIPTOR) == 0
319 || RTStrCmp(szFormatName, CFSTR_FILECONTENTS) == 0)
320 u32Format |= VBOX_SHARED_CLIPBOARD_FMT_URI_LIST;
321#endif
322 }
323 }
324 break;
325 }
326
327 if (u32Format == 0 || pCtx->pClient == NULL)
328 {
329 /* Unsupported clipboard format is requested. */
330 Log(("WM_RENDERFORMAT unsupported format requested or client is not active.\n"));
331 EmptyClipboard ();
332 }
333 else
334 {
335 int vboxrc = vboxClipboardReadDataFromClient(pCtx, u32Format);
336
337 LogFunc(("vboxClipboardReadDataFromClient vboxrc = %d, pv %p, cb %d, u32Format %d\n",
338 vboxrc, pCtx->pClient->data.pv, pCtx->pClient->data.cb, pCtx->pClient->data.u32Format));
339
340 if ( RT_SUCCESS (vboxrc)
341 && pCtx->pClient->data.pv != NULL
342 && pCtx->pClient->data.cb > 0
343 && pCtx->pClient->data.u32Format == u32Format)
344 {
345 HANDLE hMem = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, pCtx->pClient->data.cb);
346
347 LogFunc(("hMem %p\n", hMem));
348
349 if (hMem)
350 {
351 void *pMem = GlobalLock(hMem);
352
353 LogFunc(("pMem %p, GlobalSize %d\n", pMem, GlobalSize(hMem)));
354
355 if (pMem)
356 {
357 Log(("WM_RENDERFORMAT setting data\n"));
358
359 if (pCtx->pClient->data.pv)
360 {
361 memcpy(pMem, pCtx->pClient->data.pv, pCtx->pClient->data.cb);
362
363 RTMemFree(pCtx->pClient->data.pv);
364 pCtx->pClient->data.pv = NULL;
365 }
366
367 pCtx->pClient->data.cb = 0;
368 pCtx->pClient->data.u32Format = 0;
369
370 /* The memory must be unlocked before inserting to the Clipboard. */
371 GlobalUnlock(hMem);
372
373 /* 'hMem' contains the host clipboard data.
374 * size is 'cb' and format is 'format'.
375 */
376 HANDLE hClip = SetClipboardData(format, hMem);
377
378 LogFunc(("vboxClipboardHostEvent hClip %p\n", hClip));
379
380 if (hClip)
381 {
382 /* The hMem ownership has gone to the system. Nothing to do. */
383 break;
384 }
385 }
386
387 GlobalFree(hMem);
388 }
389 }
390
391 RTMemFree(pCtx->pClient->data.pv);
392 pCtx->pClient->data.pv = NULL;
393 pCtx->pClient->data.cb = 0;
394 pCtx->pClient->data.u32Format = 0;
395
396 /* Something went wrong. */
397 VBoxClipboardWinClear();
398 }
399 } break;
400
401 case WM_RENDERALLFORMATS:
402 {
403 Log(("WM_RENDERALLFORMATS\n"));
404
405 /* Do nothing. The clipboard formats will be unavailable now, because the
406 * windows is to be destroyed and therefore the guest side becomes inactive.
407 */
408 int vboxrc = VBoxClipboardWinOpen(hwnd);
409 if (RT_SUCCESS(vboxrc))
410 {
411 VBoxClipboardWinClear();
412 VBoxClipboardWinClose();
413 }
414 else
415 {
416 LogFlow(("vboxClipboardWndProc: WM_RENDERALLFORMATS: error in open clipboard. hwnd: %x, rc: %Rrc\n", hwnd, vboxrc));
417 }
418 } break;
419
420 case VBOX_CLIPBOARD_WM_SET_FORMATS:
421 {
422 if (pCtx->pClient == NULL || pCtx->pClient->fMsgFormats)
423 {
424 /* Host has pending formats message. Ignore the guest announcement,
425 * because host clipboard has more priority.
426 */
427 Log(("VBOX_CLIPBOARD_WM_SET_FORMATS ignored\n"));
428 break;
429 }
430
431 /* Announce available formats. Do not insert data, they will be inserted in WM_RENDER*. */
432 uint32_t u32Formats = (uint32_t)lParam;
433
434 Log(("VBOX_CLIPBOARD_WM_SET_FORMATS: u32Formats=%02X\n", u32Formats));
435
436 int vboxrc = VBoxClipboardWinOpen(hwnd);
437 if (RT_SUCCESS(vboxrc))
438 {
439 VBoxClipboardWinClear();
440
441 Log(("VBOX_CLIPBOARD_WM_SET_FORMATS emptied clipboard\n"));
442
443 HANDLE hClip = NULL;
444
445 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
446 hClip = SetClipboardData(CF_UNICODETEXT, NULL);
447
448 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
449 hClip = SetClipboardData(CF_DIB, NULL);
450
451 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_HTML)
452 {
453 UINT format = RegisterClipboardFormat ("HTML Format");
454 if (format != 0)
455 {
456 hClip = SetClipboardData (format, NULL);
457 }
458 }
459
460#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
461 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_URI_LIST)
462 {
463 UINT format = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
464 if (format)
465 hClip = SetClipboardData(format, NULL);
466 }
467#endif
468 VBoxClipboardWinClose();
469
470 LogFunc(("VBOX_CLIPBOARD_WM_SET_FORMATS: hClip=%p, lastErr=%ld\n", hClip, GetLastError ()));
471 }
472 } break;
473
474 case WM_DESTROY:
475 {
476 /* MS recommends to remove from Clipboard chain in this callback. */
477 VBoxClipboardWinRemoveFromCBChain(&pCtx->Win);
478 if (pWinCtx->oldAPI.timerRefresh)
479 {
480 Assert(pWinCtx->hWnd);
481 KillTimer(pWinCtx->hWnd, 0);
482 }
483 PostQuitMessage(0);
484 } break;
485
486 default:
487 {
488 Log(("WM_ %p\n", msg));
489 rc = DefWindowProc(hwnd, msg, wParam, lParam);
490 }
491 }
492
493 Log(("WM_ rc %d\n", rc));
494 return rc;
495}
496
497DECLCALLBACK(int) VBoxClipboardThread(RTTHREAD hThreadSelf, void *pvUser)
498{
499 RT_NOREF2(hThreadSelf, pvUser);
500 /* Create a window and make it a clipboard viewer. */
501 int rc = VINF_SUCCESS;
502
503 LogFlow(("VBoxClipboardThread\n"));
504
505 const PVBOXCLIPBOARDCONTEXT pCtx = &g_ctx;
506 const PVBOXCLIPBOARDWINCTX pWinCtx = &pCtx->Win;
507
508 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
509
510 /* Register the Window Class. */
511 WNDCLASS wc;
512 RT_ZERO(wc);
513
514 wc.style = CS_NOCLOSE;
515 wc.lpfnWndProc = vboxClipboardWndProc;
516 wc.hInstance = hInstance;
517 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
518 wc.lpszClassName = s_szClipWndClassName;
519
520 ATOM atomWindowClass = RegisterClass(&wc);
521
522 if (atomWindowClass == 0)
523 {
524 Log(("Failed to register window class\n"));
525 rc = VERR_NOT_SUPPORTED;
526 }
527 else
528 {
529 /* Create the window. */
530 pWinCtx->hWnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
531 s_szClipWndClassName, s_szClipWndClassName,
532 WS_POPUPWINDOW,
533 -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
534 if (pWinCtx->hWnd == NULL)
535 {
536 Log(("Failed to create window\n"));
537 rc = VERR_NOT_SUPPORTED;
538 }
539 else
540 {
541 SetWindowPos(pWinCtx->hWnd, HWND_TOPMOST, -200, -200, 0, 0,
542 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
543
544 VBoxClipboardWinAddToCBChain(&pCtx->Win);
545 if (!VBoxClipboardWinIsNewAPI(&pWinCtx->newAPI))
546 pWinCtx->oldAPI.timerRefresh = SetTimer(pWinCtx->hWnd, 0, 10 * 1000, NULL);
547
548 MSG msg;
549 BOOL msgret = 0;
550 while ((msgret = GetMessage(&msg, NULL, 0, 0)) > 0)
551 {
552 TranslateMessage(&msg);
553 DispatchMessage(&msg);
554 }
555 /*
556 * Window procedure can return error,
557 * but this is exceptional situation
558 * that should be identified in testing
559 */
560 Assert(msgret >= 0);
561 Log(("VBoxClipboardThread Message loop finished. GetMessage returned %d, message id: %d \n", msgret, msg.message));
562 }
563 }
564
565 pWinCtx->hWnd = NULL;
566
567 if (atomWindowClass != 0)
568 {
569 UnregisterClass(s_szClipWndClassName, hInstance);
570 atomWindowClass = 0;
571 }
572
573 return 0;
574}
575
576/**
577 * Synchronizes the host and the guest clipboard formats by sending all supported host clipboard
578 * formats to the guest.
579 *
580 * @returns VBox status code.
581 * @param pCtx Clipboard context to synchronize.
582 */
583static int vboxClipboardSyncInternal(PVBOXCLIPBOARDCONTEXT pCtx)
584{
585 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
586
587 if (pCtx->pClient == NULL) /* If we don't have any client data (yet), bail out. */
588 return VINF_SUCCESS;
589
590 uint32_t uFormats;
591 int rc = VBoxClipboardWinGetFormats(&pCtx->Win, &uFormats);
592 if (RT_SUCCESS(rc))
593 vboxSvcClipboardReportMsg(pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS, uFormats);
594
595 return rc;
596}
597
598/*
599 * Public platform dependent functions.
600 */
601int vboxClipboardInit(void)
602{
603 int rc = VINF_SUCCESS;
604
605 RT_ZERO(g_ctx);
606
607 /* Check that new Clipboard API is available. */
608 VBoxClipboardWinCheckAndInitNewAPI(&g_ctx.Win.newAPI);
609
610 g_ctx.hRenderEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
611
612 rc = RTThreadCreate(&g_ctx.hThread, VBoxClipboardThread, NULL, _64K /* Stack size */,
613 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
614
615 if (RT_FAILURE(rc))
616 {
617 CloseHandle(g_ctx.hRenderEvent);
618 }
619
620 return rc;
621}
622
623void vboxClipboardDestroy(void)
624{
625 Log(("vboxClipboardDestroy\n"));
626
627 if (g_ctx.Win.hWnd)
628 {
629 PostMessage(g_ctx.Win.hWnd, WM_CLOSE, 0, 0);
630 }
631
632 CloseHandle(g_ctx.hRenderEvent);
633
634 /* Wait for the window thread to terminate. */
635 RTThreadWait(g_ctx.hThread, RT_INDEFINITE_WAIT, NULL);
636
637 g_ctx.hThread = NIL_RTTHREAD;
638}
639
640int vboxClipboardConnect(VBOXCLIPBOARDCLIENTDATA *pClient, bool fHeadless)
641{
642 NOREF(fHeadless);
643 Log(("vboxClipboardConnect\n"));
644
645 if (g_ctx.pClient != NULL)
646 {
647 /* One client only. */
648 return VERR_NOT_SUPPORTED;
649 }
650
651 pClient->pCtx = &g_ctx;
652
653 pClient->pCtx->pClient = pClient;
654
655 /* Sync the host clipboard content with the client. */
656 vboxClipboardSync(pClient);
657
658 return VINF_SUCCESS;
659}
660
661int vboxClipboardSync(VBOXCLIPBOARDCLIENTDATA *pClient)
662{
663 /* Sync the host clipboard content with the client. */
664 return vboxClipboardSyncInternal(pClient->pCtx);
665}
666
667void vboxClipboardDisconnect(VBOXCLIPBOARDCLIENTDATA *pClient)
668{
669 RT_NOREF1(pClient);
670 Log(("vboxClipboardDisconnect\n"));
671
672 g_ctx.pClient = NULL;
673}
674
675void vboxClipboardFormatAnnounce(VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats)
676{
677 AssertPtrReturnVoid(pClient);
678 AssertPtrReturnVoid(pClient->pCtx);
679
680 /*
681 * The guest announces formats. Forward to the window thread.
682 */
683 PostMessage(pClient->pCtx->Win.hWnd, WM_USER, 0, u32Formats);
684}
685
686int DumpHtml(const char *pszSrc, size_t cb)
687{
688 size_t cchIgnored = 0;
689 int rc = RTStrNLenEx(pszSrc, cb, &cchIgnored);
690 if (RT_SUCCESS(rc))
691 {
692 char *pszBuf = (char *)RTMemAllocZ(cb + 1);
693 if (pszBuf != NULL)
694 {
695 rc = RTStrCopy(pszBuf, cb + 1, (const char *)pszSrc);
696 if (RT_SUCCESS(rc))
697 {
698 for (size_t i = 0; i < cb; ++i)
699 if (pszBuf[i] == '\n' || pszBuf[i] == '\r')
700 pszBuf[i] = ' ';
701 }
702 else
703 Log(("Error in copying string.\n"));
704 Log(("Removed \\r\\n: %s\n", pszBuf));
705 RTMemFree(pszBuf);
706 }
707 else
708 {
709 rc = VERR_NO_MEMORY;
710 Log(("Not enough memory to allocate buffer.\n"));
711 }
712 }
713 return rc;
714}
715
716int vboxClipboardReadData(VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format, void *pv, uint32_t cb, uint32_t *pcbActual)
717{
718 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
719 AssertPtrReturn(pClient->pCtx, VERR_INVALID_POINTER);
720
721 LogFlow(("vboxClipboardReadData: u32Format = %02X\n", u32Format));
722
723 HANDLE hClip = NULL;
724
725 const PVBOXCLIPBOARDWINCTX pWinCtx = &pClient->pCtx->Win;
726
727 /*
728 * The guest wants to read data in the given format.
729 */
730 int rc = VBoxClipboardWinOpen(pWinCtx->hWnd);
731 if (RT_SUCCESS(rc))
732 {
733 LogFunc(("Clipboard opened.\n"));
734
735 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
736 {
737 hClip = GetClipboardData(CF_DIB);
738
739 if (hClip != NULL)
740 {
741 LPVOID lp = GlobalLock(hClip);
742
743 if (lp != NULL)
744 {
745 LogFunc(("CF_DIB\n"));
746
747 vboxClipboardGetData(VBOX_SHARED_CLIPBOARD_FMT_BITMAP, lp, GlobalSize(hClip),
748 pv, cb, pcbActual);
749
750 GlobalUnlock(hClip);
751 }
752 else
753 {
754 hClip = NULL;
755 }
756 }
757 }
758 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
759 {
760 hClip = GetClipboardData(CF_UNICODETEXT);
761
762 if (hClip != NULL)
763 {
764 LPWSTR uniString = (LPWSTR)GlobalLock(hClip);
765
766 if (uniString != NULL)
767 {
768 LogFunc(("CF_UNICODETEXT\n"));
769
770 vboxClipboardGetData(VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, uniString, (lstrlenW(uniString) + 1) * 2,
771 pv, cb, pcbActual);
772
773 GlobalUnlock(hClip);
774 }
775 else
776 {
777 hClip = NULL;
778 }
779 }
780 }
781 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML)
782 {
783 UINT format = RegisterClipboardFormat("HTML Format");
784
785 if (format != 0)
786 {
787 hClip = GetClipboardData(format);
788
789 if (hClip != NULL)
790 {
791 LPVOID lp = GlobalLock(hClip);
792
793 if (lp != NULL)
794 {
795 LogFunc(("CF_HTML\n"));
796
797 vboxClipboardGetData(VBOX_SHARED_CLIPBOARD_FMT_HTML, lp, GlobalSize(hClip),
798 pv, cb, pcbActual);
799 LogRelFlowFunc(("Raw HTML clipboard data from host :"));
800 DumpHtml((char *)pv, cb);
801 GlobalUnlock(hClip);
802 }
803 else
804 {
805 hClip = NULL;
806 }
807 }
808 }
809 }
810#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
811 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_URI_LIST)
812 {
813 #if 0
814 UINT format = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
815 if (format)
816 {
817 hClip = GetClipboardData(format);
818 if (hClip != NULL)
819 {
820 LPVOID lp = GlobalLock(hClip);
821
822 if (lp != NULL)
823 {
824 LogFunc(("CF_HTML\n"));
825
826 vboxClipboardGetData(VBOX_SHARED_CLIPBOARD_FMT_HTML, lp, GlobalSize(hClip),
827 pv, cb, pcbActual);
828 LogRelFlowFunc(("Raw HTML clipboard data from host :"));
829 DumpHtml((char *)pv, cb);
830 GlobalUnlock(hClip);
831 }
832 else
833 {
834 hClip = NULL;
835 }
836 }
837 }
838 #else
839 /* Convert to a string list, separated by \r\n. */
840 DROPFILES *pDropFiles = (DROPFILES *)hClip;
841 AssertPtr(pDropFiles);
842
843 /* Do we need to do Unicode stuff? */
844 const bool fUnicode = RT_BOOL(pDropFiles->fWide);
845
846 /* Get the offset of the file list. */
847 Assert(pDropFiles->pFiles >= sizeof(DROPFILES));
848
849 /* Note: This is *not* pDropFiles->pFiles! DragQueryFile only
850 * will work with the plain storage medium pointer! */
851 HDROP hDrop = (HDROP)(hClip);
852
853 /* First, get the file count. */
854 /** @todo Does this work on Windows 2000 / NT4? */
855 char *pszFiles = NULL;
856 uint32_t cchFiles = 0;
857 UINT cFiles = DragQueryFile(hDrop, UINT32_MAX /* iFile */, NULL /* lpszFile */, 0 /* cchFile */);
858
859 LogRel(("DnD: Got %RU16 file(s), fUnicode=%RTbool\n", cFiles, fUnicode));
860
861 for (UINT i = 0; i < cFiles; i++)
862 {
863 UINT cchFile = DragQueryFile(hDrop, i /* File index */, NULL /* Query size first */, 0 /* cchFile */);
864 Assert(cchFile);
865
866 if (RT_FAILURE(rc))
867 break;
868
869 char *pszFileUtf8 = NULL; /* UTF-8 version. */
870 UINT cchFileUtf8 = 0;
871 if (fUnicode)
872 {
873 /* Allocate enough space (including terminator). */
874 WCHAR *pwszFile = (WCHAR *)RTMemAlloc((cchFile + 1) * sizeof(WCHAR));
875 if (pwszFile)
876 {
877 const UINT cwcFileUtf16 = DragQueryFileW(hDrop, i /* File index */,
878 pwszFile, cchFile + 1 /* Include terminator */);
879
880 AssertMsg(cwcFileUtf16 == cchFile, ("cchFileUtf16 (%RU16) does not match cchFile (%RU16)\n",
881 cwcFileUtf16, cchFile));
882 RT_NOREF(cwcFileUtf16);
883
884 rc = RTUtf16ToUtf8(pwszFile, &pszFileUtf8);
885 if (RT_SUCCESS(rc))
886 {
887 cchFileUtf8 = (UINT)strlen(pszFileUtf8);
888 Assert(cchFileUtf8);
889 }
890
891 RTMemFree(pwszFile);
892 }
893 else
894 rc = VERR_NO_MEMORY;
895 }
896 else /* ANSI */
897 {
898 /* Allocate enough space (including terminator). */
899 pszFileUtf8 = (char *)RTMemAlloc((cchFile + 1) * sizeof(char));
900 if (pszFileUtf8)
901 {
902 cchFileUtf8 = DragQueryFileA(hDrop, i /* File index */,
903 pszFileUtf8, cchFile + 1 /* Include terminator */);
904
905 AssertMsg(cchFileUtf8 == cchFile, ("cchFileUtf8 (%RU16) does not match cchFile (%RU16)\n",
906 cchFileUtf8, cchFile));
907 }
908 else
909 rc = VERR_NO_MEMORY;
910 }
911
912 if (RT_SUCCESS(rc))
913 {
914 LogFlowFunc(("\tFile: %s (cchFile=%RU16)\n", pszFileUtf8, cchFileUtf8));
915
916 LogRel(("DnD: Adding guest file '%s'\n", pszFileUtf8));
917
918 rc = RTStrAAppendExN(&pszFiles, 1 /* cPairs */, pszFileUtf8, cchFileUtf8);
919 if (RT_SUCCESS(rc))
920 cchFiles += cchFileUtf8;
921 }
922 else
923 LogRel(("DnD: Error handling file entry #%u, rc=%Rrc\n", i, rc));
924
925 if (pszFileUtf8)
926 RTStrFree(pszFileUtf8);
927
928 if (RT_FAILURE(rc))
929 break;
930
931 /* Add separation between filenames.
932 * Note: Also do this for the last element of the list. */
933 rc = RTStrAAppendExN(&pszFiles, 1 /* cPairs */, "\r\n", 2 /* Bytes */);
934 if (RT_SUCCESS(rc))
935 cchFiles += 2; /* Include \r\n */
936 }
937
938 if (RT_SUCCESS(rc))
939 {
940 cchFiles += 1; /* Add string termination. */
941 uint32_t cbFiles = cchFiles * sizeof(char);
942
943 LogFlowFunc(("cFiles=%u, cchFiles=%RU32, cbFiles=%RU32, pszFiles=0x%p\n",
944 cFiles, cchFiles, cbFiles, pszFiles));
945
946 /* Translate the list into URI elements. */
947 DnDURIList lstURI;
948 rc = lstURI.AppendNativePathsFromList(pszFiles, cbFiles,
949 DNDURILIST_FLAGS_ABSOLUTE_PATHS);
950 if (RT_SUCCESS(rc))
951 {
952 RTCString strRoot = lstURI.GetRootEntries();
953 size_t cbRoot = strRoot.length() + 1; /* Include termination */
954
955 mpvData = RTMemAlloc(cbRoot);
956 if (mpvData)
957 {
958 memcpy(mpvData, strRoot.c_str(), cbRoot);
959 mcbData = cbRoot;
960 }
961 else
962 rc = VERR_NO_MEMORY;
963 }
964 }
965
966 LogFlowFunc(("Building CF_HDROP list rc=%Rrc, pszFiles=0x%p, cFiles=%RU16, cchFiles=%RU32\n",
967 rc, pszFiles, cFiles, cchFiles));
968
969 if (pszFiles)
970 RTStrFree(pszFiles);
971 break;
972 #endif /* 0 */
973 }
974#endif /* VBOX_WITH_SHARED_CLIPBOARD_URI_LIST */
975 VBoxClipboardWinClose();
976 }
977 else
978 {
979 LogFunc(("vboxClipboardReadData: failed to open clipboard, rc: %Rrc\n", rc));
980 }
981
982 if (hClip == NULL)
983 {
984 /* Reply with empty data. */
985 vboxClipboardGetData(0, NULL, 0,
986 pv, cb, pcbActual);
987 }
988
989 return VINF_SUCCESS;
990}
991
992void vboxClipboardWriteData(VBOXCLIPBOARDCLIENTDATA *pClient, void *pv, uint32_t cb, uint32_t u32Format)
993{
994 LogFlow(("vboxClipboardWriteData\n"));
995
996 /*
997 * The guest returns data that was requested in the WM_RENDERFORMAT handler.
998 */
999 Assert(pClient->data.pv == NULL && pClient->data.cb == 0 && pClient->data.u32Format == 0);
1000
1001 vboxClipboardDump(pv, cb, u32Format);
1002
1003 if (cb > 0)
1004 {
1005 char *pszResult = NULL;
1006
1007 if ( u32Format == VBOX_SHARED_CLIPBOARD_FMT_HTML
1008 && !IsWindowsHTML((const char*)pv))
1009 {
1010 /* check that this is not already CF_HTML */
1011 uint32_t cbResult;
1012 int rc = ConvertMimeToCFHTML((const char *)pv, cb, &pszResult, &cbResult);
1013 if (RT_SUCCESS(rc))
1014 {
1015 if (pszResult != NULL && cbResult != 0)
1016 {
1017 pClient->data.pv = pszResult;
1018 pClient->data.cb = cbResult;
1019 pClient->data.u32Format = u32Format;
1020 }
1021 }
1022 }
1023 else
1024 {
1025 pClient->data.pv = RTMemDup(pv, cb);
1026 if (pClient->data.pv)
1027 {
1028 pClient->data.cb = cb;
1029 pClient->data.u32Format = u32Format;
1030 }
1031 }
1032 }
1033
1034 SetEvent(pClient->pCtx->hRenderEvent);
1035}
1036
1037
1038/**
1039 * Extracts field value from CF_HTML struct
1040 *
1041 * @returns VBox status code
1042 * @param pszSrc source in CF_HTML format
1043 * @param pszOption Name of CF_HTML field
1044 * @param puValue Where to return extracted value of CF_HTML field
1045 */
1046static int GetHeaderValue(const char *pszSrc, const char *pszOption, uint32_t *puValue)
1047{
1048 int rc = VERR_INVALID_PARAMETER;
1049
1050 Assert(pszSrc);
1051 Assert(pszOption);
1052
1053 const char *pszOptionValue = RTStrStr(pszSrc, pszOption);
1054 if (pszOptionValue)
1055 {
1056 size_t cchOption = strlen(pszOption);
1057 Assert(cchOption);
1058
1059 rc = RTStrToUInt32Ex(pszOptionValue + cchOption, NULL, 10, puValue);
1060 }
1061 return rc;
1062}
1063
1064
1065/**
1066 * Check that the source string contains CF_HTML struct
1067 *
1068 * @param pszSource source string.
1069 *
1070 * @returns @c true if the @a pszSource string is in CF_HTML format
1071 */
1072static bool IsWindowsHTML(const char *pszSource)
1073{
1074 return RTStrStr(pszSource, "Version:") != NULL
1075 && RTStrStr(pszSource, "StartHTML:") != NULL;
1076}
1077
1078
1079/**
1080 * Converts clipboard data from CF_HTML format to MIME clipboard format.
1081 *
1082 * Returns allocated buffer that contains html converted to text/html mime type
1083 *
1084 * @returns VBox status code.
1085 * @param pszSource The input.
1086 * @param cch The length of the input.
1087 * @param ppszOutput Where to return the result. Free using RTMemFree.
1088 * @param pcbOutput Where to the return length of the result (bytes/chars).
1089 */
1090static int ConvertCFHtmlToMime(const char *pszSource, const uint32_t cch, char **ppszOutput, uint32_t *pcbOutput)
1091{
1092 Assert(pszSource);
1093 Assert(cch);
1094 Assert(ppszOutput);
1095 Assert(pcbOutput);
1096
1097 uint32_t offStart;
1098 int rc = GetHeaderValue(pszSource, "StartFragment:", &offStart);
1099 if (RT_SUCCESS(rc))
1100 {
1101 uint32_t offEnd;
1102 rc = GetHeaderValue(pszSource, "EndFragment:", &offEnd);
1103 if (RT_SUCCESS(rc))
1104 {
1105 if ( offStart > 0
1106 && offEnd > 0
1107 && offEnd > offStart
1108 && offEnd <= cch)
1109 {
1110 uint32_t cchSubStr = offEnd - offStart;
1111 char *pszResult = (char *)RTMemAlloc(cchSubStr + 1);
1112 if (pszResult)
1113 {
1114 rc = RTStrCopyEx(pszResult, cchSubStr + 1, pszSource + offStart, cchSubStr);
1115 if (RT_SUCCESS(rc))
1116 {
1117 *ppszOutput = pszResult;
1118 *pcbOutput = (uint32_t)(cchSubStr + 1);
1119 rc = VINF_SUCCESS;
1120 }
1121 else
1122 {
1123 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc\n", rc));
1124 RTMemFree(pszResult);
1125 }
1126 }
1127 else
1128 {
1129 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment.\n"));
1130 rc = VERR_NO_MEMORY;
1131 }
1132 }
1133 else
1134 {
1135 LogRelFlowFunc(("Error: CF_HTML out of bounds - offStart=%#x offEnd=%#x cch=%#x\n", offStart, offEnd, cch));
1136 rc = VERR_INVALID_PARAMETER;
1137 }
1138 }
1139 else
1140 {
1141 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc.\n", rc));
1142 rc = VERR_INVALID_PARAMETER;
1143 }
1144 }
1145 else
1146 {
1147 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected StartFragment. rc = %Rrc.\n", rc));
1148 rc = VERR_INVALID_PARAMETER;
1149 }
1150
1151 return rc;
1152}
1153
1154
1155
1156/**
1157 * Converts source UTF-8 MIME HTML clipboard data to UTF-8 CF_HTML format.
1158 *
1159 * This is just encapsulation work, slapping a header on the data.
1160 *
1161 * It allocates
1162 *
1163 * Calculations:
1164 * Header length = format Length + (2*(10 - 5('%010d'))('digits')) - 2('%s') = format length + 8
1165 * EndHtml = Header length + fragment length
1166 * StartHtml = 105(constant)
1167 * StartFragment = 141(constant) may vary if the header html content will be extended
1168 * EndFragment = Header length + fragment length - 38(ending length)
1169 *
1170 * @param pszSource Source buffer that contains utf-16 string in mime html format
1171 * @param cb Size of source buffer in bytes
1172 * @param ppszOutput Where to return the allocated output buffer to put converted UTF-8
1173 * CF_HTML clipboard data. This function allocates memory for this.
1174 * @param pcbOutput Where to return the size of allocated result buffer in bytes/chars, including zero terminator
1175 *
1176 * @note output buffer should be free using RTMemFree()
1177 * @note Everything inside of fragment can be UTF8. Windows allows it. Everything in header should be Latin1.
1178 */
1179static int ConvertMimeToCFHTML(const char *pszSource, size_t cb, char **ppszOutput, uint32_t *pcbOutput)
1180{
1181 Assert(ppszOutput);
1182 Assert(pcbOutput);
1183 Assert(pszSource);
1184 Assert(cb);
1185
1186 /* construct CF_HTML formatted string */
1187 char *pszResult = NULL;
1188 size_t cchFragment;
1189 int rc = RTStrNLenEx(pszSource, cb, &cchFragment);
1190 if (!RT_SUCCESS(rc))
1191 {
1192 LogRelFlowFunc(("Error: invalid source fragment. rc = %Rrc.\n"));
1193 return VERR_INVALID_PARAMETER;
1194 }
1195
1196 /*
1197 @StartHtml - pos before <html>
1198 @EndHtml - whole size of text excluding ending zero char
1199 @StartFragment - pos after <!--StartFragment-->
1200 @EndFragment - pos before <!--EndFragment-->
1201 @note: all values includes CR\LF inserted into text
1202 Calculations:
1203 Header length = format Length + (3*6('digits')) - 2('%s') = format length + 16 (control value - 183)
1204 EndHtml = Header length + fragment length
1205 StartHtml = 105(constant)
1206 StartFragment = 143(constant)
1207 EndFragment = Header length + fragment length - 40(ending length)
1208 */
1209 static const char s_szFormatSample[] =
1210 /* 0: */ "Version:1.0\r\n"
1211 /* 13: */ "StartHTML:000000101\r\n"
1212 /* 34: */ "EndHTML:%0000009u\r\n" // END HTML = Header length + fragment length
1213 /* 53: */ "StartFragment:000000137\r\n"
1214 /* 78: */ "EndFragment:%0000009u\r\n"
1215 /* 101: */ "<html>\r\n"
1216 /* 109: */ "<body>\r\n"
1217 /* 117: */ "<!--StartFragment-->"
1218 /* 137: */ "%s"
1219 /* 137+2: */ "<!--EndFragment-->\r\n"
1220 /* 157+2: */ "</body>\r\n"
1221 /* 166+2: */ "</html>\r\n";
1222 /* 175+2: */
1223 AssertCompile(sizeof(s_szFormatSample) == 175 + 2 + 1);
1224
1225 /* calculate parameters of CF_HTML header */
1226 size_t cchHeader = sizeof(s_szFormatSample) - 1;
1227 size_t offEndHtml = cchHeader + cchFragment;
1228 size_t offEndFragment = cchHeader + cchFragment - 38; /* 175-137 = 38 */
1229 pszResult = (char *)RTMemAlloc(offEndHtml + 1);
1230 if (pszResult == NULL)
1231 {
1232 LogRelFlowFunc(("Error: Cannot allocate memory for result buffer. rc = %Rrc.\n"));
1233 return VERR_NO_MEMORY;
1234 }
1235
1236 /* format result CF_HTML string */
1237 size_t cchFormatted = RTStrPrintf(pszResult, offEndHtml + 1,
1238 s_szFormatSample, offEndHtml, offEndFragment, pszSource);
1239 Assert(offEndHtml == cchFormatted); NOREF(cchFormatted);
1240
1241#ifdef VBOX_STRICT
1242 /* Control calculations. check consistency.*/
1243 static const char s_szStartFragment[] = "<!--StartFragment-->";
1244 static const char s_szEndFragment[] = "<!--EndFragment-->";
1245
1246 /* check 'StartFragment:' value */
1247 const char *pszRealStartFragment = RTStrStr(pszResult, s_szStartFragment);
1248 Assert(&pszRealStartFragment[sizeof(s_szStartFragment) - 1] - pszResult == 137);
1249
1250 /* check 'EndFragment:' value */
1251 const char *pszRealEndFragment = RTStrStr(pszResult, s_szEndFragment);
1252 Assert((size_t)(pszRealEndFragment - pszResult) == offEndFragment);
1253#endif
1254
1255 *ppszOutput = pszResult;
1256 *pcbOutput = (uint32_t)cchFormatted + 1;
1257 Assert(*pcbOutput == cchFormatted + 1);
1258
1259 return VINF_SUCCESS;
1260}
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