VirtualBox

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

Last change on this file since 61589 was 61589, checked in by vboxsync, 9 years ago

Additions/SharedClipboard: fix for ​bugref:8363: added HTML clipboard support to Linux. Added ability to copy HTML clipboard data between Windows and Linux.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette