VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/VBoxClipboard-win.cpp@ 63495

Last change on this file since 63495 was 63122, checked in by vboxsync, 8 years ago

vboxClipboardDump,DumpHtml: Warnings (always check that the for loop counter and limit are somewhat compative). RTStrCopy will always terminate the result, so give it the full size so it won't truncate a string that's missing the terminator.

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